diff options
author | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2008-08-29 12:11:34 +0000 |
---|---|---|
committer | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2008-08-29 12:11:34 +0000 |
commit | 32d17447a258188b2d534bcb0bf65a659ba7b7d0 (patch) | |
tree | 4ad8bb267eb29f7091a7da283f6d7eec1e2188e1 /smcc | |
download | mocca-32d17447a258188b2d534bcb0bf65a659ba7b7d0.tar.gz mocca-32d17447a258188b2d534bcb0bf65a659ba7b7d0.tar.bz2 mocca-32d17447a258188b2d534bcb0bf65a659ba7b7d0.zip |
Initial import.
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@1 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'smcc')
27 files changed, 2452 insertions, 0 deletions
diff --git a/smcc/keystore.ks b/smcc/keystore.ks Binary files differnew file mode 100644 index 00000000..824c3a40 --- /dev/null +++ b/smcc/keystore.ks diff --git a/smcc/pom.xml b/smcc/pom.xml new file mode 100644 index 00000000..3e3dfe14 --- /dev/null +++ b/smcc/pom.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>bku</artifactId>
+ <groupId>at.gv.egiz</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>at.gv.egiz</groupId>
+ <artifactId>smcc</artifactId>
+ <name>smcc</name>
+ <packaging>jar</packaging>
+ <version>1.0-SNAPSHOT</version>
+ <url>http://bku.egiz.gv.at</url>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <archive>
+ <addMavenDescriptor>false</addMavenDescriptor>
+ <index>false</index>
+ <manifest>
+ <addClasspath>false</addClasspath>
+ </manifest>
+ </archive>
+ <alias>test-applet signer</alias>
+ <keystore>keystore.ks</keystore>
+ <storepass>storepass</storepass>
+ <keypass>keypass</keypass>
+ <verify>true</verify>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <configuration>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <!-- FIXME just for testing
+ <dependency>
+ <groupId>iaik</groupId>
+ <artifactId>iaik_jce_full_signed</artifactId>
+ </dependency> -->
+ </dependencies>
+</project>
\ No newline at end of file diff --git a/smcc/src/main/java/META-INF/MANIFEST.MF b/smcc/src/main/java/META-INF/MANIFEST.MF new file mode 100644 index 00000000..5e949512 --- /dev/null +++ b/smcc/src/main/java/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0
+Class-Path:
+
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java new file mode 100644 index 00000000..7269ba7f --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -0,0 +1,310 @@ +//Copyright (C) 2002 IAIK +//http://jce.iaik.at +// +//Copyright (C) 2003 Stiftung Secure Information and +// Communication Technologies SIC +//http://www.sic.st +// +//All rights reserved. +// +//This source is provided for inspection purposes and recompilation only, +//unless specified differently in a contract with IAIK. This source has to +//be kept in strict confidence and must not be disclosed to any third party +//under any circumstances. Redistribution in source and binary forms, with +//or without modification, are <not> permitted in any case! +// +//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. +// +// +package at.gv.egiz.smcc; + +import java.nio.charset.Charset; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +public class ACOSCard extends AbstractSignatureCard implements SignatureCard { + + public static final byte[] AID_DEC = new byte[] { (byte) 0xA0, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x4E }; + + public static final byte[] DF_DEC = new byte[] { (byte) 0xdf, (byte) 0x71 }; + + public static final byte[] AID_SIG = new byte[] { (byte) 0xA0, (byte) 0x00, + (byte) 0x00, (byte) 0x01, (byte) 0x18, (byte) 0x45, (byte) 0x43 }; + + public static final byte[] DF_SIG = new byte[] { (byte) 0xdf, (byte) 0x70 }; + + public static final byte[] EF_C_CH_EKEY = new byte[] { (byte) 0xc0, + (byte) 0x01 }; + + public static final int EF_C_CH_EKEY_MAX_SIZE = 2000; + + public static final byte[] EF_C_CH_DS = new byte[] { (byte) 0xc0, (byte) 0x02 }; + + public static final int EF_C_CH_DS_MAX_SIZE = 2000; + + public static final byte[] EF_PK_CH_EKEY = new byte[] { (byte) 0xb0, + (byte) 0x01 }; + + public static final byte[] EF_INFOBOX = new byte[] { (byte) 0xc0, (byte) 0x02 }; + + public static final int EF_INFOBOX_MAX_SIZE = 1500; + + public static final byte KID_PIN_SIG = (byte) 0x81; + + public static final byte KID_PIN_DEC = (byte) 0x81; + + public static final byte KID_PIN_INF = (byte) 0x83; + + public static final byte[] DST_SIG = new byte[] { (byte) 0x84, (byte) 0x01, // tag + // , + // length + // ( + // key + // ID + // ) + (byte) 0x88, // SK.CH.SIGN + (byte) 0x80, (byte) 0x01, // tag, length (algorithm ID) + (byte) 0x14 // ECDSA + }; + + public static final byte[] DST_DEC = new byte[] { (byte) 0x84, (byte) 0x01, // tag + // , + // length + // ( + // key + // ID + // ) + (byte) 0x88, // SK.CH.EKEY + (byte) 0x80, (byte) 0x01, // tag, length (algorithm ID) + (byte) 0x01 // RSA // TODO: Not verified yet + }; + + public ACOSCard() { + super("at/gv/egiz/smcc/ACOSCard"); + } + + byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04, + 0x00, fid, 256)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("Failed to select file (AID=" + + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + "."); + } else { + return resp.getBytes(); + } + } + + byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00, + 0x00, fid, 256)); + if (resp.getSW() == 0x6a82) { + throw new SignatureCardException("Failed to select file (FID=" + + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + ")"); + } else { + return resp.getBytes(); + } + } + + /** + * + * @param pinProvider + * @param spec + * the PIN spec to be given to the pinProvider + * @param kid + * the KID (key identifier) of the PIN to be verified + * @param kfpc + * acutal value of the KFCP (key fault presentation counter) or less + * than 0 if actual value is unknown + * + * @return -1 if the PIN has been verifyed successfully, or else the new value + * of the KFCP (key fault presentation counter) + * + * @throws CancelledException + * if the user canceld the operation + * @throws javax.smartcardio.CardException + * @throws at.gv.egiz.smcc.SignatureCardException + */ + int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc) + throws CardException, CancelledException, SignatureCardException { + + CardChannel channel = getCardChannel(); + + // get PIN + String pin = pinProvider.providePIN(spec, kfpc); + if (pin == null) { + // User canceld operation + // throw new CancelledException("User canceld PIN entry"); + return -2; + } + + logger.finest("PIN=" + pin); + + byte[] asciiPIN = pin.getBytes(Charset.forName("ASCII")); + byte[] encodedPIN = new byte[8]; + System.arraycopy(asciiPIN, 0, encodedPIN, 0, Math.min(asciiPIN.length, + encodedPIN.length)); + + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, + kid, encodedPIN)); + if (resp.getSW1() == (byte) 0x63 && resp.getSW2() >> 4 == (byte) 0xc) { + return resp.getSW2() & (byte) 0x0f; + } else if (resp.getSW() == 0x6983) { + // PIN blocked + throw new SignatureCardException(spec.getLocalizedName() + " blocked."); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException("Failed to verify pin: SW=" + + Integer.toHexString(resp.getSW()) + "."); + } else { + return -1; + } + + } + + void mseSetDST(byte[] dst) throws CardException, SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, 0x81, + 0xB6, dst)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("MSE:SET DST failed: SW=" + + Integer.toHexString(resp.getSW())); + } + } + + void psoHash(byte[] hash) throws CardException, SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x90, + 0x81, hash)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("PSO:HASH failed: SW=" + + Integer.toHexString(resp.getSW())); + } + } + + byte[] psoComputDigitalSiganture() throws CardException, + SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x9E, + 0x9A, 256)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException( + "PSO: COMPUTE DIGITAL SIGNATRE failed: SW=" + + Integer.toHexString(resp.getSW())); + } else { + return resp.getData(); + } + } + + public byte[] getCertificate(KeyboxName keyboxName) + throws SignatureCardException { + + if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { + return readTLVFile(AID_SIG, EF_C_CH_DS, EF_C_CH_DS_MAX_SIZE); + } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { + return readTLVFile(AID_DEC, EF_C_CH_EKEY, EF_C_CH_EKEY_MAX_SIZE); + } else { + throw new IllegalArgumentException("Keybox " + keyboxName + + " not supported."); + } + + } + + public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + throws SignatureCardException { + + if ("IdentityLink".equals(infobox)) { + + PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString( + "inf.pin.name")); + try { + byte[] res = readTLVFilePIN(AID_DEC, EF_INFOBOX, KID_PIN_INF, provider, + spec, EF_INFOBOX_MAX_SIZE); + return res; + } catch (Exception e) { + throw new SecurityException(e); + } + + } else { + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); + } + + } + + public String toString() { + return "a-sign premium"; + } + + public byte[] createSignature(byte[] hash, KeyboxName keyboxName, + PINProvider provider) throws SignatureCardException { + + if (hash.length != 20) { + throw new IllegalArgumentException("Hash value must be of length 20"); + } + + byte[] fid; + byte kid; + byte[] dst; + PINSpec spec; + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { + fid = DF_SIG; + kid = KID_PIN_SIG; + dst = DST_SIG; + spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString( + "sig.pin.name")); + + } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { + fid = DF_DEC; + kid = KID_PIN_DEC; + dst = DST_DEC; + spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString( + "dec.pin.name")); + + } else { + throw new IllegalArgumentException("KeyboxName '" + keyboxName + + "' not supported."); + } + + try { + + // SELECT DF + selectFileFID(fid); + // VERIFY + int kfpc = -1; + while (true) { + kfpc = verifyPIN(provider, spec, kid, kfpc); + if (kfpc < -1) { + return null; + } else if (kfpc < 0) { + break; + } + } + // MSE: SET DST + mseSetDST(dst); + // PSO: HASH + psoHash(hash); + // PSO: COMPUTE DIGITAL SIGNATURE + byte[] rs = psoComputDigitalSiganture(); + + return rs; + + } catch (CardException e) { + throw new SignatureCardException("Failed to create signature.", e); + } + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java new file mode 100644 index 00000000..91c873c9 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -0,0 +1,259 @@ +//Copyright (C) 2002 IAIK +//http://jce.iaik.at +// +//Copyright (C) 2003 Stiftung Secure Information and +// Communication Technologies SIC +//http://www.sic.st +// +//All rights reserved. +// +//This source is provided for inspection purposes and recompilation only, +//unless specified differently in a contract with IAIK. This source has to +//be kept in strict confidence and must not be disclosed to any third party +//under any circumstances. Redistribution in source and binary forms, with +//or without modification, are <not> permitted in any case! +// +//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. +// +// +package at.gv.egiz.smcc; + +import java.nio.ByteBuffer; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.logging.Logger; + +import javax.smartcardio.ATR; +import javax.smartcardio.Card; +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +public abstract class AbstractSignatureCard implements SignatureCard { + + static Logger logger = Logger.getLogger(AbstractSignatureCard.class.getName()); + + private ResourceBundle i18n; + private String resourceBundleName; + + private Locale locale = Locale.getDefault(); + + int ifs_ = 254; + + Card card_; + + protected AbstractSignatureCard(String resourceBundleName) { + this.resourceBundleName = resourceBundleName; + } + + String toString(byte[] b) { + StringBuffer sb = new StringBuffer(); + if (b != null && b.length > 0) { + sb.append(Integer.toHexString((b[0] & 240) >> 4)); + sb.append(Integer.toHexString(b[0] & 15)); + } + for(int i = 1; i < b.length; i++) { + sb.append(':'); + sb.append(Integer.toHexString((b[i] & 240) >> 4)); + sb.append(Integer.toHexString(b[i] & 15)); + } + return sb.toString(); + } + + abstract byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException; + + abstract byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException; + + byte[] readBinary(CardChannel channel, int offset, int len) + throws CardException, SignatureCardException { + + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xB0, + 0x7F & (offset >> 8), offset & 0xFF, len)); + if (resp.getSW() == 0x9000) { + return resp.getData(); + } else { + throw new SignatureCardException("Failed to read bytes (" + offset + "+" + + len + "): SW=" + Integer.toHexString(resp.getSW())); + } + + } + + int readBinary(int offset, int len, byte[] b) + throws CardException, SignatureCardException { + + if (b.length < len) { + throw new IllegalArgumentException( + "Length of b must not be less than len."); + } + + CardChannel channel = getCardChannel(); + + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xB0, + 0x7F & (offset >> 8), offset & 0xFF, len)); + if (resp.getSW() == 0x9000) { + System.arraycopy(resp.getData(), 0, b, 0, len); + } + + return resp.getSW(); + + } + + byte[] readBinaryTLV(int maxSize, byte expectedType) throws CardException, SignatureCardException { + + CardChannel channel = getCardChannel(); + + // read first chunk + int len = Math.min(maxSize, ifs_); + byte[] chunk = readBinary(channel, 0, len); + if (chunk.length > 0 && chunk[0] != expectedType) { + return null; + } + int offset = chunk.length; + int actualSize = maxSize; + if (chunk.length > 3) { + if ((chunk[1] & 0x80) > 0) { + int octets = (0x0F & chunk[1]); + actualSize = 2 + octets; + for (int i = 1; i <= octets; i++) { + actualSize += (0xFF & chunk[i + 1]) << ((octets - i) * 8); + } + } else { + actualSize = 2 + chunk[1]; + } + } + ByteBuffer buffer = ByteBuffer.allocate(actualSize); + buffer.put(chunk, 0, Math.min(actualSize, chunk.length)); + while (offset < actualSize) { + len = Math.min(ifs_, actualSize - offset); + chunk = readBinary(channel, offset, len); + buffer.put(chunk); + offset += chunk.length; + } + return buffer.array(); + + } + + abstract int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc) throws CardException, SignatureCardException; + + public byte[] readTLVFile(byte[] aid, byte[] ef, int maxLength) throws SignatureCardException { + return readTLVFilePIN(aid, ef, (byte) 0, null, null, maxLength); + } + + public byte[] readTLVFilePIN(byte[] aid, byte[] ef, byte kid, + PINProvider provider, PINSpec spec, int maxLength) throws SignatureCardException { + + try { + + // SELECT FILE (AID) + byte[] rb = selectFileAID(aid); + if (rb[rb.length - 2] != (byte) 0x90 || + rb[rb.length - 1] != (byte) 0x00) { + + throw new SignatureCardException( + "SELECT FILE with " + + "AID=" + toString(aid) + " failed (" + + "SW=" + + Integer.toHexString( + (0xFF & (int) rb[rb.length - 1]) | + (0xFF & (int) rb[rb.length - 2]) << 8) + + ")."); + + } + + // SELECT FILE (EF) + rb = selectFileFID(ef); + if (rb[rb.length - 2] != (byte) 0x90 || + rb[rb.length - 1] != (byte) 0x00) { + + throw new SignatureCardException( + "SELECT FILE with " + + "FID=" + toString(ef) + " failed (" + + "SW=" + + Integer.toHexString( + (0xFF & (int) rb[rb.length - 1]) | + (0xFF & (int) rb[rb.length - 2]) << 8) + + ")."); + } + + // try to READ BINARY + int sw = readBinary(0, 1, new byte[1]); + if (provider != null && sw == 0x6982) { + + // VERIFY + int kfpc = -1; // unknown + while (true) { + kfpc = verifyPIN(provider, spec, kid, kfpc); + if (kfpc < -1) { + return null; + } else if (kfpc < 0) { + break; + } + } + } else if (sw != 0x9000) { + throw new SignatureCardException("READ BINARY failed (SW=" + + Integer.toHexString(sw) + ")."); + } + + // READ BINARY + byte[] data = readBinaryTLV(maxLength, (byte) 0x30); + + return data; + + + } catch (CardException e) { + throw new SignatureCardException("Failed to acces card.", e); + } + + } + + + ResponseAPDU transmit(CardChannel channel, CommandAPDU commandAPDU) throws CardException { + logger.fine(commandAPDU + "\n" + toString(commandAPDU.getBytes())); + long t0 = System.currentTimeMillis(); + ResponseAPDU responseAPDU = channel.transmit(commandAPDU); + long t1 = System.currentTimeMillis(); + logger.fine(responseAPDU + "\n[" + (t1 - t0) + "ms] " + toString(responseAPDU.getBytes())); + return responseAPDU; + } + + public void init(Card card) { + card_ = card; + ATR atr = card.getATR(); + byte[] atrBytes = atr.getBytes(); + if (atrBytes.length >= 6) { + ifs_ = 0xFF & atr.getBytes()[6]; + logger.finer("Setting IFS (information field size) to " + ifs_); + } + } + + CardChannel getCardChannel() { + return card_.getBasicChannel(); + } + + + @Override + public void setLocale(Locale locale) { + if (locale == null) { + throw new NullPointerException("Locale must not be set to null"); + } + this.locale = locale; + } + + protected ResourceBundle getResourceBundle() { + if (i18n == null) { + i18n = ResourceBundle.getBundle(resourceBundleName, locale); + } + return i18n; + } +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java b/smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java new file mode 100644 index 00000000..347d74c9 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/CancelledException.java @@ -0,0 +1,39 @@ +/* +* 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 CancelledException extends SignatureCardException {
+
+ private static final long serialVersionUID = 1L;
+
+ public CancelledException() {
+ super();
+ }
+
+ public CancelledException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public CancelledException(String message) {
+ super(message);
+ }
+
+ public CancelledException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java b/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java new file mode 100644 index 00000000..e2a5fe16 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/CardNotSupportedException.java @@ -0,0 +1,74 @@ +//Copyright (C) 2002 IAIK +//http://jce.iaik.at +// +//Copyright (C) 2003 Stiftung Secure Information and +// Communication Technologies SIC +//http://www.sic.st +// +//All rights reserved. +// +//This source is provided for inspection purposes and recompilation only, +//unless specified differently in a contract with IAIK. This source has to +//be kept in strict confidence and must not be disclosed to any third party +//under any circumstances. Redistribution in source and binary forms, with +//or without modification, are <not> permitted in any case! +// +//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. +// +// +package at.gv.egiz.smcc; + +public class CardNotSupportedException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Creates a new instance of this <code>CardNotSupportedException</code>. + * + */ + public CardNotSupportedException() { + super(); + } + + /** + * Creates a new instance of this <code>CardNotSupportedException</code>. + * + * @param message + * @param cause + */ + public CardNotSupportedException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Creates a new instance of this <code>CardNotSupportedException</code>. + * + * @param message + */ + public CardNotSupportedException(String message) { + super(message); + } + + /** + * Creates a new instance of this <code>CardNotSupportedException</code>. + * + * @param cause + */ + public CardNotSupportedException(Throwable cause) { + super(cause); + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java new file mode 100644 index 00000000..844115a4 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINProvider.java @@ -0,0 +1,35 @@ +//Copyright (C) 2002 IAIK +//http://jce.iaik.at +// +//Copyright (C) 2003 Stiftung Secure Information and +// Communication Technologies SIC +//http://www.sic.st +// +//All rights reserved. +// +//This source is provided for inspection purposes and recompilation only, +//unless specified differently in a contract with IAIK. This source has to +//be kept in strict confidence and must not be disclosed to any third party +//under any circumstances. Redistribution in source and binary forms, with +//or without modification, are <not> permitted in any case! +// +//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. +// +// +package at.gv.egiz.smcc; + +public interface PINProvider { + + public String providePIN(PINSpec spec, int retries); + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java new file mode 100644 index 00000000..cc54a337 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java @@ -0,0 +1,85 @@ +/* +* 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. +*/ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.smcc; + +import java.util.ResourceBundle; + +/** + * + * @author mcentner + */ +public class PINSpec { + + int minLength_ = 0; + + int maxLength_ = -1; + + String rexepPattern_; + + ResourceBundle resourceBundle_; + + String name_; + + public PINSpec(int minLenght, int maxLength, String rexepPattern, + ResourceBundle resourceBundle, String name) { + + minLength_ = minLenght; + maxLength_ = maxLength; + rexepPattern_ = rexepPattern; + resourceBundle_ = resourceBundle; + name_ = name; + } + + public PINSpec(int minLenght, int maxLength, String rexepPattern, + String name) { + + minLength_ = minLenght; + maxLength_ = maxLength; + rexepPattern_ = rexepPattern; + name_ = name; + } + + + + public String getLocalizedName() { + + return (resourceBundle_ != null) + ? resourceBundle_.getString(name_) + : name_; + + } + + public int getMaxLength() { + return maxLength_; + } + + public int getMinLength() { + return minLength_; + } + + public String getRexepPattern() { + return rexepPattern_; + } + + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java new file mode 100644 index 00000000..79e2663e --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -0,0 +1,341 @@ +//Copyright (C) 2002 IAIK +//http://jce.iaik.at +// +//Copyright (C) 2003 Stiftung Secure Information and +// Communication Technologies SIC +//http://www.sic.st +// +//All rights reserved. +// +//This source is provided for inspection purposes and recompilation only, +//unless specified differently in a contract with IAIK. This source has to +//be kept in strict confidence and must not be disclosed to any third party +//under any circumstances. Redistribution in source and binary forms, with +//or without modification, are <not> permitted in any case! +// +//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. +// +// +package at.gv.egiz.smcc; + +import java.math.BigInteger; +import java.util.Arrays; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +public class STARCOSCard extends AbstractSignatureCard implements SignatureCard { + + public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 }; + + public static final byte[] AID_INFOBOX = new byte[] { (byte) 0xd0, + (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, + (byte) 0x18, (byte) 0x01 }; + + public static final byte[] EF_INFOBOX = new byte[] { (byte) 0xef, (byte) 0x01 }; + + public static final byte[] AID_SVSIG_CERT = new byte[] { (byte) 0xd0, + (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, + (byte) 0x10, (byte) 0x01 }; + + public static final byte[] EF_SVSIG_CERT_CA = new byte[] { (byte) 0x2f, + (byte) 0x01 }; + + public static final byte[] EF_SVSIG_CERT = new byte[] { (byte) 0x2f, + (byte) 0x02 }; + + // Sichere Signatur (SS) + + public static final byte[] AID_DF_SS = new byte[] { (byte) 0xd0, (byte) 0x40, + (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x12, + (byte) 0x01 }; + + public static final byte[] EF_C_X509_CH_DS = new byte[] { (byte) 0xc0, + (byte) 0x00 }; + + public static final byte[] EF_C_X509_CA_CS_DS = new byte[] { (byte) 0xc6, + (byte) 0x08 }; + + public static final byte[] DST_SS = new byte[] { (byte) 0x84, (byte) 0x03, // tag + // , + // length + // ( + // key + // desc + // . + // ) + (byte) 0x80, (byte) 0x02, (byte) 0x00, // local, key ID, key version + (byte) 0x89, (byte) 0x03, // tag, length (algorithm ID) + (byte) 0x13, (byte) 0x35, (byte) 0x10 // ECDSA + }; + + public static final byte KID_PIN_SS = (byte) 0x81; + + // Gew�hnliche Signatur (GS) + + public static final byte[] AID_DF_GS = new byte[] { (byte) 0xd0, (byte) 0x40, + (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x13, + (byte) 0x01 }; + + public static final byte[] EF_C_X509_CH_AUT = new byte[] { (byte) 0x2f, + (byte) 0x01 }; + + public static final byte[] EF_C_X509_CA_CS = new byte[] { (byte) 0x2f, + (byte) 0x02 }; + + public static final byte[] DST_GS = new byte[] { (byte) 0x84, (byte) 0x03, // tag + // , + // length + // ( + // key + // desc + // . + // ) + (byte) 0x80, (byte) 0x02, (byte) 0x00, // local, key ID, key version + (byte) 0x89, (byte) 0x01, // tag, length (algorithm ID) + (byte) 0x14 // ECDSA + }; + + public static final byte KID_PIN_CARD = (byte) 0x01; + + public STARCOSCard() { + super("at/gv/egiz/smcc/STARCOSCard"); + } + + public byte[] getCertificate(KeyboxName keyboxName) + throws SignatureCardException { + + if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { + return readTLVFile(AID_DF_SS, EF_C_X509_CH_DS, 2000); + } else if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { + return readTLVFile(AID_DF_GS, EF_C_X509_CH_AUT, 2000); + } else { + throw new IllegalArgumentException("Keybox " + keyboxName + + " not supported."); + } + + } + + byte[] selectFileAID(byte[] fid) throws CardException, SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x04, + 0x04, fid, 256)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("Failed to select file (AID=" + + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + "."); + } else { + return resp.getBytes(); + } + } + + void selectMF() throws CardException, SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x00, + 0x0C)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("Failed to select MF: SW=" + + Integer.toHexString(resp.getSW()) + "."); + } + } + + byte[] selectFileFID(byte[] fid) throws CardException, SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0xA4, 0x02, + 0x04, fid, 256)); + if (resp.getSW() == 0x6a82) { + throw new SignatureCardException("Failed to select file (FID=" + + toString(fid) + "): SW=" + Integer.toHexString(resp.getSW()) + "."); + } else { + return resp.getBytes(); + } + } + + void mseSetDST(byte[] dst) throws CardException, SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x22, 0x41, + 0xB6, dst)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("MSE:SET DST failed: SW=" + + Integer.toHexString(resp.getSW())); + } + } + + void psoHash(byte[] hash) throws CardException, SignatureCardException { + byte[] data = new byte[hash.length + 2]; + data[0] = (byte) 0x90; // tag + data[1] = (byte) (hash.length); // length + System.arraycopy(hash, 0, data, 2, hash.length); + + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x90, + 0xA0, data)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("PSO:HASH failed: SW=" + + Integer.toHexString(resp.getSW())); + } + } + + byte[] psoComputDigitalSiganture() throws CardException, + SignatureCardException { + CardChannel channel = getCardChannel(); + ResponseAPDU resp = transmit(channel, new CommandAPDU(0x00, 0x2A, 0x9E, + 0x9A, 256)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException( + "PSO: COMPUTE DIGITAL SIGNATRE failed: SW=" + + Integer.toHexString(resp.getSW())); + } else { + return resp.getData(); + } + } + + int verifyPIN(PINProvider pinProvider, PINSpec spec, byte kid, int kfpc) + throws CardException, SignatureCardException { + + CardChannel channel = getCardChannel(); + + // get number of possible retries + ResponseAPDU resp = transmit(channel, + new CommandAPDU(0x00, 0x20, 0x00, kid)); + int retries; + if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) { + retries = resp.getSW2() & 0x0f; + } else if (resp.getSW() == 0x6984) { + // PIN LCS = "Initilized" (not activated) + throw new SignatureCardException(spec.getLocalizedName() + " not set."); + } else { + throw new SignatureCardException("Failed to get PIN retries: SW=" + + Integer.toHexString(resp.getSW())); + } + + // get PIN + String pin = pinProvider.providePIN(spec, retries); + if (pin == null) { + // User canceled operation + // throw new CancelledException("User canceld PIN entry"); + return -2; + } + // PIN length in bytes + int len = (int) Math.ceil(pin.length() / 2); + + // BCD encode PIN and marshal PIN block + byte[] pinBytes = new BigInteger(pin, 16).toByteArray(); + byte[] pinBlock = new byte[8]; + if (len < pinBytes.length) { + System.arraycopy(pinBytes, pinBytes.length - len, pinBlock, 1, len); + } else { + System.arraycopy(pinBytes, 0, pinBlock, len - pinBytes.length + 1, + pinBytes.length); + } + pinBlock[0] = (byte) (0x20 + len * 2); + Arrays.fill(pinBlock, len + 1, 8, (byte) 0xff); + + resp = transmit(channel, new CommandAPDU(0x00, 0x20, 0x00, kid, pinBlock)); + if (resp.getSW1() == 0x63 && resp.getSW2() >> 4 == 0xc) { + return resp.getSW2() & 0x0f; + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException("Failed to verify pin: SW=" + + Integer.toHexString(resp.getSW())); + } else { + return -1; + } + + } + + public byte[] createSignature(byte[] hash, KeyboxName keyboxName, + PINProvider provider) throws SignatureCardException { + + if (hash.length != 20) { + throw new IllegalArgumentException("Hash value must be of length 20"); + } + + byte[] aid; + byte kid; + byte[] dst; + PINSpec spec; + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { + aid = AID_DF_SS; + kid = KID_PIN_SS; + dst = DST_SS; + spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); + + } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { + aid = AID_DF_GS; + kid = KID_PIN_CARD; + dst = DST_GS; + spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); + + } else { + throw new IllegalArgumentException("KeyboxName '" + keyboxName + + "' not supported."); + } + + try { + + // SELECT MF + selectMF(); + // SELECT DF + selectFileAID(aid); + // VERIFY + int retr = -1; // unknown + while (true) { + retr = verifyPIN(provider, spec, kid, retr); + if (retr < -1) { + return null; + } else if (retr < 0) { + break; + } + } + // MSE: SET DST + mseSetDST(dst); + // PSO: HASH + psoHash(hash); + // PSO: COMPUTE DIGITAL SIGNATURE + byte[] rs = psoComputDigitalSiganture(); + return rs; + + } catch (CardException e) { + throw new SignatureCardException("Failed to create signature.", e); + } + + } + + public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + throws SignatureCardException { + + if ("IdentityLink".equals(infobox)) { + + PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); + try { + byte[] res = readTLVFilePIN(AID_INFOBOX, EF_INFOBOX, KID_PIN_CARD, + provider, spec, 2000); + return res; + } catch (Exception e) { + throw new SignatureCardException(e); + } + } else { + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); + } + + } + + public String toString() { + return "eCard"; + } + + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java new file mode 100644 index 00000000..f19bc709 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -0,0 +1,322 @@ +/* +* 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. +*/ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package at.gv.egiz.smcc; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.util.Enumeration; +import java.util.Locale; + +import javax.smartcardio.Card; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author mcentner + */ +public class SWCard implements SignatureCard { + + private static final String BKU_USER_DIR = ".bku"; + + private static final String SWCARD_DIR = "smcc"; + + private static final String KEYSTORE_CERTIFIED_KEYPAIR = "certified.p12"; + + private static final String CERTIFICATE_CERTIFIED_KEYPAIR = "certified.cer"; + + private static final String KEYSTORE_SECURE_KEYPAIR = "secure.p12"; + + private static final String CERTIFICATE_SECURE_KEYPAIR = "secure.cer"; + + private static String swCardDir; + + private static Log log = LogFactory.getLog(SWCard.class); + + private KeyStore certifiedKeyStore; + + private KeyStore secureKeyStore; + + private Certificate certifiedCertificate; + + private Certificate secureCertificate; + + static { + String userHome = System.getProperty("user.home"); + String fs = System.getProperty("file.separator"); + swCardDir = userHome + fs + BKU_USER_DIR + fs + SWCARD_DIR; + } + + /** + * @return the swCardDir + */ + public static String getSwCardDir() { + return swCardDir; + } + + /** + * @param swCardDir the swCardDir to set + */ + public static void setSwCardDir(String swCardDir) { + SWCard.swCardDir = swCardDir; + } + + public void init(Card card) { + } + + private String getFileName(String fileName) { + String fs = System.getProperty("file.separator"); + return swCardDir + fs + fileName; + } + + private Certificate loadCertificate(String certificateFileName) throws SignatureCardException { + + final String certificateType = "x509"; + CertificateFactory factory; + try { + factory = CertificateFactory.getInstance(certificateType); + } catch (CertificateException e) { + String msg = "Failed to get CertificateFactory instance for type '" + certificateType + "'."; + log.error(msg, e); + throw new SignatureCardException(msg, e); + } + + // try to load Certificate file + String fileName = getFileName(certificateFileName); + log.info("Trying to load Certificate from file '" + fileName + "'."); + + FileInputStream certificateFile; + try { + certificateFile = new FileInputStream(fileName); + } catch (FileNotFoundException e) { + String msg = "Certificate file '" + fileName + "' not found."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } + + Certificate certificate; + try { + certificate = factory.generateCertificate(certificateFile); + } catch (CertificateException e) { + String msg = "Failed to load Certificate from file '" + fileName + "'."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } + + return certificate; + + } + + private KeyStore loadKeyStore(String keyStoreFileName, char[] password) throws SignatureCardException { + + final String keyStoreType = "pkcs12"; + KeyStore keyStore; + try { + keyStore = KeyStore.getInstance(keyStoreType); + } catch (KeyStoreException e) { + String msg = "Failed to get KeyStore instance for KeyStore type '" + keyStoreType + "'."; + log.error(msg, e); + throw new SignatureCardException(msg, e); + } + + // try to load KeyStore file + String fileName = getFileName(keyStoreFileName); + log.info("Trying to load KeyStore from file '" + fileName + "'."); + + FileInputStream keyStoreFile; + try { + keyStoreFile = new FileInputStream(fileName); + } catch (FileNotFoundException e) { + String msg = "KeyStore file '"+ fileName + "' not found."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } + + try { + keyStore.load(keyStoreFile, null); + } catch (Exception e) { + String msg = "Failed to load KeyStore from file '" + fileName + "'."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } + + return keyStore; + + + } + + private KeyStore getKeyStore(KeyboxName keyboxName, char[] password) throws SignatureCardException { + + if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { + if (certifiedKeyStore == null) { + certifiedKeyStore = loadKeyStore(KEYSTORE_CERTIFIED_KEYPAIR, password); + } + return certifiedKeyStore; + } else if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { + if (secureKeyStore == null) { + secureKeyStore = loadKeyStore(KEYSTORE_SECURE_KEYPAIR, password); + } + return secureKeyStore; + } else { + throw new SignatureCardException("Keybox of type '" + keyboxName + "' not supported."); + } + + } + + + public byte[] getCertificate(KeyboxName keyboxName) + throws SignatureCardException { + + try { + if (keyboxName == KeyboxName.CERITIFIED_KEYPAIR) { + if (certifiedCertificate == null) { + certifiedCertificate = loadCertificate(CERTIFICATE_CERTIFIED_KEYPAIR); + } + return certifiedCertificate.getEncoded(); + } else if (keyboxName == KeyboxName.SECURE_SIGNATURE_KEYPAIR) { + if (secureCertificate == null) { + secureCertificate = loadCertificate(CERTIFICATE_SECURE_KEYPAIR); + } + return secureCertificate.getEncoded(); + } else { + throw new SignatureCardException("Keybox of type '" + keyboxName + "' not supported."); + } + } catch (CertificateEncodingException e) { + throw new SignatureCardException("Failed to get encoded Certificate.", e); + } + + + } + + public byte[] getInfobox(String infobox, PINProvider provider, String domainId) throws SignatureCardException { + + String fileName = getFileName(infobox + ".ibx"); + FileInputStream file; + try { + file = new FileInputStream(fileName); + } catch (FileNotFoundException e) { + String msg = "Infobox '" + infobox + "' not found."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try { + byte[] b = new byte[512]; + for(int l; (l = file.read(b)) != -1;) { + bytes.write(b, 0, l); + } + file.close(); + } catch (IOException e) { + String msg = "Failed to read infobox '" + infobox + "'."; + log.error(msg, e); + throw new SignatureCardException(msg, e); + } + + return bytes.toByteArray(); + + } + + public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException { + + // KeyStore password + PINSpec pinSpec = new PINSpec(0, -1, ".", "KeyStore-Password"); + + KeyStore keyStore = getKeyStore(keyboxName, null); + + PrivateKey privateKey = null; + + try { + for (Enumeration<String> aliases = keyStore.aliases(); aliases + .hasMoreElements() && privateKey == null;) { + String alias = aliases.nextElement(); + log.debug("Found alias '" + alias + "' in keystore"); + if (keyStore.isKeyEntry(alias)) { + Key key = null; + while (key == null) { + try { + String pin = provider.providePIN(pinSpec, -1); + key = keyStore.getKey(alias, pin.toCharArray()); + } catch (UnrecoverableKeyException e) { + log.info("Failed to get Key from KeyStore. Wrong password?", e); + } + } + privateKey = (PrivateKey) key; + } + } + } catch (Exception e) { + String msg = "Failed to get certificate from KeyStore."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } + + if (privateKey == null) { + String msg = "No private key found in KeyStore."; + log.info(msg); + throw new SignatureCardException(msg); + } + + String algorithm = privateKey.getAlgorithm(); + algorithm = "SHA1with" + algorithm; + try { + Signature signature = Signature.getInstance(algorithm); + signature.initSign(privateKey); + signature.update(hash); + return signature.sign(); + } catch (NoSuchAlgorithmException e) { + String msg = "Algorithm + '" + algorithm + "' not supported for signing."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } catch (SignatureException e) { + String msg = "Signing faild."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } catch (InvalidKeyException e) { + String msg = "Key not valid for algorithm + '" + algorithm + "'."; + log.info(msg, e); + throw new SignatureCardException(msg, e); + } + + } + + @Override + public void setLocale(Locale locale) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java new file mode 100644 index 00000000..18a63514 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java @@ -0,0 +1,103 @@ +//Copyright (C) 2002 IAIK +//http://jce.iaik.at +// +//Copyright (C) 2003 Stiftung Secure Information and +// Communication Technologies SIC +//http://www.sic.st +// +//All rights reserved. +// +//This source is provided for inspection purposes and recompilation only, +//unless specified differently in a contract with IAIK. This source has to +//be kept in strict confidence and must not be disclosed to any third party +//under any circumstances. Redistribution in source and binary forms, with +//or without modification, are <not> permitted in any case! +// +//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. +// +// +package at.gv.egiz.smcc; + +import java.util.Locale; + +import javax.smartcardio.Card; + +public interface SignatureCard { + + public static class KeyboxName { + + public static KeyboxName SECURE_SIGNATURE_KEYPAIR = new KeyboxName( + "SecureSignatureKeypair"); + public static KeyboxName CERITIFIED_KEYPAIR = new KeyboxName( + "CertifiedKeypair"); + + private String keyboxName_; + + private KeyboxName(String keyboxName_) { + this.keyboxName_ = keyboxName_; + } + + public static KeyboxName getKeyboxName(String keyBox) { + if (SECURE_SIGNATURE_KEYPAIR.equals(keyBox)) { + return SECURE_SIGNATURE_KEYPAIR; + } else if (CERITIFIED_KEYPAIR.equals(keyBox)) { + return CERITIFIED_KEYPAIR; + } else { + return new KeyboxName(keyBox); + } + } + + public boolean equals(Object obj) { + if (obj instanceof String) { + return obj.equals(keyboxName_); + } + if (obj instanceof KeyboxName) { + return ((KeyboxName) obj).keyboxName_.equals(keyboxName_); + } else { + return super.equals(obj); + } + } + + public String getKeyboxName() { + return keyboxName_; + } + + } + + public void init(Card card); + + public byte[] getCertificate(KeyboxName keyboxName) + throws SignatureCardException; + + /** + * + * @param infobox + * @param provider + * @param domainId may be null. + * @return + * @throws SignatureCardException + */ + public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + throws SignatureCardException; + + public byte[] createSignature(byte[] hash, KeyboxName keyboxName, + PINProvider provider) throws SignatureCardException; + + /** + * Sets the local for evtl. required callbacks (e.g. PINSpec) + * @param locale must not be null; + */ + public void setLocale(Locale locale); + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java new file mode 100644 index 00000000..f2a964fe --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardException.java @@ -0,0 +1,76 @@ +//Copyright (C) 2002 IAIK +//http://jce.iaik.at +// +//Copyright (C) 2003 Stiftung Secure Information and +// Communication Technologies SIC +//http://www.sic.st +// +//All rights reserved. +// +//This source is provided for inspection purposes and recompilation only, +//unless specified differently in a contract with IAIK. This source has to +//be kept in strict confidence and must not be disclosed to any third party +//under any circumstances. Redistribution in source and binary forms, with +//or without modification, are <not> permitted in any case! +// +//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. +// +// +package at.gv.egiz.smcc; + +public class SignatureCardException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Creates a new instance of this <code>SignatureCardException</code>. + * + */ + public SignatureCardException() { + super(); + } + + /** + * Creates a new instance of this <code>SignatureCardException</code>. + * + * @param message + * @param cause + */ + public SignatureCardException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Creates a new instance of this <code>SignatureCardException</code>. + * + * @param message + */ + public SignatureCardException(String message) { + super(message); + } + + /** + * Creates a new instance of this <code>SignatureCardException</code>. + * + * @param cause + */ + public SignatureCardException(Throwable cause) { + super(cause); + } + + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java new file mode 100644 index 00000000..2131a737 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -0,0 +1,97 @@ +//Copyright (C) 2002 IAIK +//http://jce.iaik.at +// +//Copyright (C) 2003 Stiftung Secure Information and +// Communication Technologies SIC +//http://www.sic.st +// +//All rights reserved. +// +//This source is provided for inspection purposes and recompilation only, +//unless specified differently in a contract with IAIK. This source has to +//be kept in strict confidence and must not be disclosed to any third party +//under any circumstances. Redistribution in source and binary forms, with +//or without modification, are <not> permitted in any case! +// +//THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. +// +// +package at.gv.egiz.smcc; + +import javax.smartcardio.ATR; +import javax.smartcardio.Card; + +public class SignatureCardFactory { + + public static SignatureCardFactory getInstance() { + return new SignatureCardFactory(); + } + + private SignatureCardFactory() { + + } + + public SignatureCard createSignatureCard(Card card) + throws CardNotSupportedException { + + if(card == null) { + SignatureCard sCard = new SWCard(); + sCard.init(card); + return sCard; + } + + ATR atr = card.getATR(); + byte[] historicalBytes = atr.getHistoricalBytes(); + if(historicalBytes == null || historicalBytes.length < 3) { + throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes())); + } + + int t = ((0xFF & (int) historicalBytes[0]) << 16) + + ((0xFF & (int) historicalBytes[1]) << 8) + + (0xFF & (int) historicalBytes[2]); + + SignatureCard sCard; + switch (t) { + case 0x455041 : + case 0x4D4341 : + sCard = new ACOSCard(); + break; + + case 0x805102 : + sCard = new STARCOSCard(); + break; + + default : + throw new CardNotSupportedException("Card not supported: ATR=" + toString(atr.getBytes())); + } + sCard.init(card); + return sCard; + + } + + public static String toString(byte[] b) { + StringBuffer sb = new StringBuffer(); + if (b != null && b.length > 0) { + sb.append(Integer.toHexString((b[0] & 240) >> 4)); + sb.append(Integer.toHexString(b[0] & 15)); + } + for(int i = 1; i < b.length; i++) { + sb.append(':'); + sb.append(Integer.toHexString((b[i] & 240) >> 4)); + sb.append(Integer.toHexString(b[i] & 15)); + } + return sb.toString(); + } + + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java new file mode 100644 index 00000000..3d1fd7c7 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java @@ -0,0 +1,150 @@ +/* +* 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.util;
+
+import java.util.Locale;
+import java.util.Map;
+
+import javax.smartcardio.ATR;
+import javax.smartcardio.Card;
+import javax.smartcardio.CardTerminal;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import at.gv.egiz.smcc.CardNotSupportedException;
+import at.gv.egiz.smcc.SignatureCard;
+import at.gv.egiz.smcc.SignatureCardFactory;
+
+public class SMCCHelper {
+
+ public final static int NO_CARD = 0;
+ public final static int PC_SC_NOT_SUPPORTED = 1;
+ public final static int TERMINAL_NOT_PRESENT = 2;
+ public final static int CARD_NOT_SUPPORTED = 3;
+ public final static int CARD_FOUND = 4;
+
+ private final static Log log = LogFactory.getLog(SMCCHelper.class);
+
+ protected SmartCardIO smartCardIO = new SmartCardIO();
+ protected int resultCode = NO_CARD;
+ protected SignatureCard signatureCard = null;
+ protected static boolean useSWCard = false;
+
+ public SMCCHelper() {
+ update();
+ }
+
+ public void update() {
+ update(-1);
+ }
+
+ public void update(int sleep) {
+ SignatureCardFactory factory = SignatureCardFactory.getInstance();
+ if (useSWCard) {
+ try {
+ signatureCard = factory.createSignatureCard(null);
+ resultCode = CARD_FOUND;
+ } catch (CardNotSupportedException e) {
+ resultCode = CARD_NOT_SUPPORTED;
+ signatureCard = null;
+ }
+ return;
+ }
+ signatureCard = null;
+ resultCode = NO_CARD;
+ // find pcsc support
+ if (smartCardIO.isPCSCSupported()) {
+ // find supported card
+ if (smartCardIO.isTerminalPresent()) {
+ Map<CardTerminal, Card> newCards = null;
+ if (sleep > 0) {
+ smartCardIO.waitForInserted(sleep);
+
+ }
+ newCards = smartCardIO.getCards();
+ for (CardTerminal cardTerminal : newCards.keySet()) {
+ try {
+ Card c = newCards.get(cardTerminal);
+ if (c == null) {
+ throw new CardNotSupportedException();
+ }
+ signatureCard = factory.createSignatureCard(c);
+ ATR atr = newCards.get(cardTerminal).getATR();
+ log.trace("Found supported card (" + signatureCard.toString() + ") "
+ + "in terminal '" + cardTerminal.getName() + "', ATR = "
+ + toString(atr.getHistoricalBytes()) + ".");
+ resultCode = CARD_FOUND;
+ break;
+
+ } catch (CardNotSupportedException e) {
+ Card c = newCards.get(cardTerminal);
+ if (c != null) {
+ ATR atr = c.getATR();
+ log.info("Found unsupported card" + " in terminal '"
+ + cardTerminal.getName() + "', ATR = "
+ + toString(atr.getHistoricalBytes()) + ".");
+ } else {
+ log.info("Found unsupported card in terminal '"
+ + cardTerminal.getName() + "' without ATR");
+ }
+ resultCode = CARD_NOT_SUPPORTED;
+ }
+ }
+ } else {
+ resultCode = TERMINAL_NOT_PRESENT;
+ }
+ } else {
+ resultCode = PC_SC_NOT_SUPPORTED;
+ }
+ }
+
+ public SignatureCard getSignatureCard(Locale locale) {
+ if (signatureCard != null) {
+ signatureCard.setLocale(locale);
+ }
+ return signatureCard;
+ }
+
+ public int getResultCode() {
+ return resultCode;
+ }
+
+ public static String toString(byte[] b) {
+ StringBuffer sb = new StringBuffer();
+ sb.append('[');
+ if (b != null && b.length > 0) {
+ sb.append(Integer.toHexString((b[0] & 240) >> 4));
+ sb.append(Integer.toHexString(b[0] & 15));
+ for (int i = 1; i < b.length; i++) {
+ sb.append((i % 32 == 0) ? '\n' : ':');
+ sb.append(Integer.toHexString((b[i] & 240) >> 4));
+ sb.append(Integer.toHexString(b[i] & 15));
+ }
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ public static boolean isUseSWCard() {
+ return useSWCard;
+ }
+
+ public static void setUseSWCard(boolean useSWCard) {
+ SMCCHelper.useSWCard = useSWCard;
+ }
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java new file mode 100644 index 00000000..ffffd3af --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java @@ -0,0 +1,196 @@ +/* +* 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.util;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.smartcardio.Card;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.CardTerminals;
+import javax.smartcardio.TerminalFactory;
+import javax.smartcardio.CardTerminals.State;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ *
+ * @author mcentner
+ */
+public class SmartCardIO {
+
+ private static final int STATE_INITIALIZED = 1;
+
+ private static final int STATE_TERMINAL_FACTORY = 2;
+
+ private static final int STATE_TERMINALS = 3;
+
+ private static Log log = LogFactory.getLog(SmartCardIO.class);
+
+ final Map<CardTerminal, Card> terminalCard_ = new HashMap<CardTerminal, Card>();
+
+ int state_ = STATE_INITIALIZED;
+
+ TerminalFactory terminalFactory_ = null;
+
+ CardTerminals cardTerminals_;
+
+ private void updateTerminalFactory() {
+ TerminalFactory terminalFactory = TerminalFactory.getDefault();
+ log.debug("TerminalFactory : " + terminalFactory);
+ if ("PC/SC".equals(terminalFactory.getType())) {
+ terminalFactory_ = terminalFactory;
+ }
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ state_ = STATE_TERMINAL_FACTORY;
+ }
+ }
+
+ public boolean isPCSCSupported() {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ return terminalFactory_ != null;
+ }
+
+ private void updateCardTerminals() {
+ if(terminalFactory_ != null) {
+ cardTerminals_ = terminalFactory_.terminals();
+ }
+ log.debug("CardTerminals : " + cardTerminals_);
+ if (state_ < STATE_TERMINALS) {
+ state_ = STATE_TERMINALS;
+ }
+ }
+
+ public CardTerminals getCardTerminals() {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ if(state_ < STATE_TERMINALS) {
+ updateCardTerminals();
+ }
+ return cardTerminals_;
+ }
+
+ public boolean isTerminalPresent() {
+ CardTerminals cardTerminals = getCardTerminals();
+ if (cardTerminals != null) {
+ List<CardTerminal> terminals = null;
+ try {
+ terminals = cardTerminals.list(State.ALL);
+
+ // logging
+ if(log.isInfoEnabled()) {
+ if (terminals == null || terminals.isEmpty()) {
+ log.info("No card terminal found.");
+ } else {
+ StringBuffer msg = new StringBuffer();
+ msg.append("Found " + terminals.size() + " card terminal(s):");
+ for (CardTerminal terminal : terminals) {
+ msg.append("\n " + terminal.getName());
+ }
+ log.info(msg.toString());
+ }
+ }
+
+ return terminals != null && !terminals.isEmpty();
+ } catch (CardException e) {
+ log.info("Failed to list card terminals.", e);
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private Map<CardTerminal, Card> updateCards() {
+
+ // clear card references if removed
+ try {
+ log.trace("terminals.list(State.CARD_REMOVAL)");
+ for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_REMOVAL)) {
+ Card card = terminalCard_.remove(terminal);
+ log.trace("card removed : " + card);
+ }
+ } catch (CardException e) {
+ log.debug(e);
+ }
+
+ // check inserted cards
+ Map<CardTerminal, Card> newCards = new HashMap<CardTerminal, Card>();
+ try {
+ log.trace("terminals.list(State.CARD_INSERTION)");
+ for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_INSERTION)) {
+
+ Card card = null;
+ try {
+ // try to connect to card
+ card = terminal.connect("*");
+ } catch (CardException e) {
+ log.trace("Failed to connect to card.", e);
+ }
+
+ // have we seen this card before?
+ if (terminalCard_.put(terminal, card) == null) {
+ terminalCard_.put(terminal, card);
+ newCards.put(terminal, card);
+ log.trace("terminal '" + terminal + "' card inserted : " + card);
+ }
+ }
+ } catch (CardException e) {
+ log.debug(e);
+ }
+ return newCards;
+
+ }
+
+ public Map<CardTerminal, Card> getCards() {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ if(state_ < STATE_TERMINALS) {
+ updateCardTerminals();
+ }
+ updateCards();
+ Map<CardTerminal, Card> terminalCard = new HashMap<CardTerminal, Card>();
+ terminalCard.putAll(terminalCard_);
+ return Collections.unmodifiableMap(terminalCard);
+ }
+
+ public Map<CardTerminal, Card> waitForInserted(int timeout) {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ if(state_ < STATE_TERMINALS) {
+ updateCardTerminals();
+ }
+ try {
+ // just waiting for a short period of time to allow for abort
+ cardTerminals_.waitForChange(timeout);
+ } catch (CardException e) {
+ log.debug("CardTerminals.waitForChange(" + timeout + ") failed.", e);
+ }
+ Map<CardTerminal, Card> newCards = new HashMap<CardTerminal, Card>();
+ newCards.putAll(updateCards());
+ return Collections.unmodifiableMap(newCards);
+ }
+}
\ No newline at end of file diff --git a/smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.java b/smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.java new file mode 100644 index 00000000..e5030da2 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/utils/SingletonPINProvider.java @@ -0,0 +1,38 @@ +/* +* 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.utils;
+
+import at.gv.egiz.smcc.PINProvider;
+import at.gv.egiz.smcc.PINSpec;
+
+public class SingletonPINProvider implements PINProvider {
+
+ private String pin;
+ private boolean pin_already_provided = false;
+
+ public SingletonPINProvider(String pin) {
+ this.pin = pin;
+ }
+
+ public String providePIN(PINSpec spec, int retries) {
+ if (pin_already_provided)
+ return null;
+ pin_already_provided = true;
+ return pin;
+ }
+
+}
diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/ACOSCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/ACOSCard.properties new file mode 100644 index 00000000..9142841c --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/ACOSCard.properties @@ -0,0 +1,21 @@ +# 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. + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +dec.pin.name=Geheimhaltungs-PIN +sig.pin.name=Signature-PIN +inf.pin.name=Infobox-PIN diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/ACOSCard_de.properties b/smcc/src/main/resources/at/gv/egiz/smcc/ACOSCard_de.properties new file mode 100644 index 00000000..d2bbe4f9 --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/ACOSCard_de.properties @@ -0,0 +1,21 @@ +# 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. + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +dec.pin.name=Geheimhaltungs-PIN +sig.pin.name=Signatur-PIN +inf.pin.name=Infobox-PIN diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/ACOSCard_en.properties b/smcc/src/main/resources/at/gv/egiz/smcc/ACOSCard_en.properties new file mode 100644 index 00000000..9142841c --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/ACOSCard_en.properties @@ -0,0 +1,21 @@ +# 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. + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +dec.pin.name=Geheimhaltungs-PIN +sig.pin.name=Signature-PIN +inf.pin.name=Infobox-PIN diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/STARCOSCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/STARCOSCard.properties new file mode 100644 index 00000000..77935333 --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/STARCOSCard.properties @@ -0,0 +1,20 @@ +# 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. + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +sig.pin.name=Signature-PIN +card.pin.name=Card-PIN diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/STARCOSCard_de.properties b/smcc/src/main/resources/at/gv/egiz/smcc/STARCOSCard_de.properties new file mode 100644 index 00000000..6fa5f0fa --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/STARCOSCard_de.properties @@ -0,0 +1,20 @@ +# 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. + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +sig.pin.name=Signatur-PIN +card.pin.name=Karten-PIN diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/STARCOSCard_en.properties b/smcc/src/main/resources/at/gv/egiz/smcc/STARCOSCard_en.properties new file mode 100644 index 00000000..77935333 --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/STARCOSCard_en.properties @@ -0,0 +1,20 @@ +# 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. + +# To change this template, choose Tools | Templates +# and open the template in the editor. + +sig.pin.name=Signature-PIN +card.pin.name=Card-PIN diff --git a/smcc/src/test/java/at/gv/egiz/smcc/SMCCApplication.java b/smcc/src/test/java/at/gv/egiz/smcc/SMCCApplication.java new file mode 100644 index 00000000..5f4bb67e --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/SMCCApplication.java @@ -0,0 +1,46 @@ +/* +* 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.util.Locale;
+
+import at.gv.egiz.smcc.util.SMCCHelper;
+
+public class SMCCApplication {
+
+ /**
+ * @param args
+ */
+ public static void main(String[] args) {
+
+ SignatureCard sc = null;
+ SMCCHelper smccHelper = new SMCCHelper();
+ while (smccHelper.getResultCode() != SMCCHelper.CARD_FOUND) {
+ System.out.println("Did not get a signature card ... "+smccHelper.getResultCode());
+ smccHelper.update();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ sc = smccHelper.getSignatureCard(Locale.getDefault());
+ System.out.println("Found supported siganture card: "+sc);
+ }
+
+}
diff --git a/smcc/src/test/java/at/gv/egiz/smcc/SWCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/SWCardTest.java new file mode 100644 index 00000000..5448fee2 --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/SWCardTest.java @@ -0,0 +1,63 @@ +/* +* 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.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import at.gv.egiz.smcc.SignatureCard.KeyboxName;
+
+public class SWCardTest implements PINProvider {
+
+ SWCard swCard = new SWCard();
+
+ public static void main(String[] args) throws Exception {
+
+ SWCardTest swCardTest = new SWCardTest();
+ swCardTest.test();
+
+ }
+
+ public void test() throws SignatureCardException, NoSuchAlgorithmException {
+
+ swCard.getCertificate(KeyboxName.CERITIFIED_KEYPAIR);
+ swCard.getCertificate(KeyboxName.SECURE_SIGNATURE_KEYPAIR);
+
+ BigInteger t = BigInteger.valueOf(System.currentTimeMillis());
+
+ MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
+ byte[] hash = messageDigest.digest(t.toByteArray());
+
+ byte[] signature;
+ signature = swCard.createSignature(hash, KeyboxName.CERITIFIED_KEYPAIR, this);
+ System.out.println(SignatureCardFactory.toString(signature));
+
+ signature = swCard.createSignature(hash, KeyboxName.SECURE_SIGNATURE_KEYPAIR, this);
+ System.out.println(SignatureCardFactory.toString(signature));
+
+ byte[] infobox = swCard.getInfobox("IdentityLink", this, null);
+ System.out.println(SignatureCardFactory.toString(infobox));
+
+ }
+
+ @Override
+ public String providePIN(PINSpec spec, int retries) {
+ return "buerger";
+ }
+
+}
diff --git a/smcc/src/test/resources/IdentityLink.bin b/smcc/src/test/resources/IdentityLink.bin Binary files differnew file mode 100644 index 00000000..16c7375b --- /dev/null +++ b/smcc/src/test/resources/IdentityLink.bin diff --git a/smcc/src/test/resources/log4j.properties b/smcc/src/test/resources/log4j.properties new file mode 100644 index 00000000..94662fd2 --- /dev/null +++ b/smcc/src/test/resources/log4j.properties @@ -0,0 +1,19 @@ +# loglever DEBUG, appender STDOUT +log4j.rootLogger=DEBUG, STDOUT +#log4j.logger.at.gv.egiz.slbinding.RedirectEventFilter=DEBUG, STDOUT + +# STDOUT appender +log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender +log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout +#log4j.appender.STDOUT.layout.ConversionPattern=%5p | %d{dd HH:mm:ss,SSS} | %20c | %10t | %m%n +#log4j.appender.STDOUT.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n +log4j.appender.STDOUT.layout.ConversionPattern=%-5p |%d | %t | %c %x- %m%n + +### FILE appender +#log4j.appender.file=org.apache.log4j.RollingFileAppender +#log4j.appender.file.maxFileSize=100KB +#log4j.appender.file.maxBackupIndex=9 +#log4j.appender.file.File=egovbus_ca.log +#log4j.appender.file.threshold=info +#log4j.appender.file.layout=org.apache.log4j.PatternLayout +#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
\ No newline at end of file |