diff options
Diffstat (limited to 'smcc/src/main/java/at/gv')
-rw-r--r-- | smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java | 55 | ||||
-rw-r--r-- | smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java | 283 | ||||
-rw-r--r-- | smcc/src/main/java/at/gv/egiz/smcc/ITCard.java | 297 | ||||
-rw-r--r-- | smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java | 192 | ||||
-rw-r--r-- | smcc/src/main/java/at/gv/egiz/smcc/SWCard.java | 8 | ||||
-rw-r--r-- | smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java | 11 | ||||
-rw-r--r-- | smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java | 96 | ||||
-rw-r--r-- | smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java | 11 |
8 files changed, 879 insertions, 74 deletions
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java index 414d4678..a63d4076 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -16,9 +16,13 @@ */ package at.gv.egiz.smcc; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.security.AlgorithmParameters; import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; @@ -375,12 +379,46 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC @Override @Exclusive - public byte[] createSignature(byte[] hash, KeyboxName keyboxName, - PINProvider provider) throws SignatureCardException, InterruptedException { + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException { - if (hash.length != 20) { - throw new IllegalArgumentException("Hash value must be of length 20."); + ByteArrayOutputStream dst = new ByteArrayOutputStream(); + // key ID + dst.write(new byte[]{(byte) 0x84, (byte) 0x01, (byte) 0x88}); + // algorithm ID + dst.write(new byte[]{(byte) 0x80, (byte) 0x01}); + + MessageDigest md; + try { + if ("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1".equals(alg)) { + dst.write((byte) 0x14); // SHA-1/ECC + md = MessageDigest.getInstance("SHA-1"); + } else if ("http://www.w3.org/2000/09/xmldsig#rsa-sha1".equals(alg)) { + dst.write((byte) 0x12); // SHA-1 with padding according to PKCS#1 block type 01 + md = MessageDigest.getInstance("SHA-1"); + } else if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName) + && appVersion >= 2 + && "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256".equals(alg)) { + dst.write((byte) 0x44); // SHA-256/ECC + md = MessageDigest.getInstance("SHA256"); + } else if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName) + && appVersion >= 2 + && "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256".equals(alg)) { + dst.write((byte) 0x41); // SHA-256 with padding according to PKCS#1 + md = MessageDigest.getInstance("SHA256"); + } else { + throw new SignatureCardException("Card does not support signature algorithm " + alg + "."); + } + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get MessageDigest.", e); + throw new SignatureCardException(e); } + + byte[] digest = new byte[md.getDigestLength()]; + for (int l; (l = input.read(digest)) != -1;) { + md.update(digest, 0, l); + } + digest = md.digest(); try { @@ -393,11 +431,11 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC // SELECT application execSELECT_AID(channel, AID_SIG); // MANAGE SECURITY ENVIRONMENT : SET DST - execMSE(channel, 0x41, 0xb6, DST_SIG); + execMSE(channel, 0x41, 0xb6, dst.toByteArray()); // VERIFY verifyPINLoop(channel, spec, provider); // PERFORM SECURITY OPERATION : HASH - execPSO_HASH(channel, hash); + execPSO_HASH(channel, digest); // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATRE return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel); @@ -413,7 +451,7 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC while (true) { try { // INTERNAL AUTHENTICATE - return execINTERNAL_AUTHENTICATE(channel, hash); + return execINTERNAL_AUTHENTICATE(channel, digest); } catch (SecurityStatusNotSatisfiedException e) { verifyPINLoop(channel, spec, provider); } @@ -711,6 +749,9 @@ public class ACOSCard extends AbstractSignatureCard implements PINMgmtSignatureC ResponseAPDU resp = channel.transmit( new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, 256)); + if (resp.getSW() == 0x6982) { + throw new SecurityStatusNotSatisfiedException(); + } if (resp.getSW() != 0x9000) { throw new SignatureCardException( "PSO - COMPUTE DIGITAL SIGNATRE failed: SW=" diff --git a/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java b/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java new file mode 100644 index 00000000..15b47fb0 --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java @@ -0,0 +1,283 @@ +/* +* 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 javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.SMCCHelper; + +public class BELPICCard extends AbstractSignatureCard implements SignatureCard { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(BELPICCard.class); + + public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 }; + + public static final byte[] DF_BELPIC = new byte[] { (byte) 0xDF, + (byte) 0x00 }; + + public static final byte[] DF_ID = new byte[] { (byte) 0xDF, (byte) 0x01 }; + + public static final byte[] SIGN_CERT = new byte[] { (byte) 0x50, + (byte) 0x39 }; + +// public static final byte MSE_SET_ALGO_REF = (byte) 0x02; + +// public static final byte MSE_SET_PRIV_KEY_REF = (byte) 0x83; + + public static final int SIGNATURE_LENGTH = (int) 0x80; + + public static final byte KID = (byte) 0x01; + + public static final int READ_BUFFER_LENGTH = 256; + + public static final int PINSPEC_SS = 0; + + private static final PINSpec SS_PIN_SPEC = + new PINSpec(4, 12, "[0-9]", + "at/gv/egiz/smcc/BelpicCard", "sig.pin", KID, DF_BELPIC); + + /** + * Creates a new instance. + */ + public BELPICCard() { + super("at/gv/egiz/smcc/BelpicCard"); + pinSpecs.add(SS_PIN_SPEC); + } + + @Override + @Exclusive + public byte[] getCertificate(KeyboxName keyboxName) + throws SignatureCardException { + + if (keyboxName != KeyboxName.SECURE_SIGNATURE_KEYPAIR) { + throw new IllegalArgumentException("Keybox " + keyboxName + + " not supported"); + } + + try { + CardChannel channel = getCardChannel(); + // SELECT MF + execSELECT_FID(channel, MF); + // SELECT application + execSELECT_FID(channel, DF_BELPIC); + // SELECT file + execSELECT_FID(channel, SIGN_CERT); + // READ BINARY + byte[] certificate = ISO7816Utils.readTransparentFileTLV(channel, -1, (byte) 0x30); + if (certificate == null) { + throw new NotActivatedException(); + } + return certificate; + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } catch (CardException e) { + log.info("Failed to get certificate.", e); + throw new SignatureCardException(e); + } + + } + + @Override + @Exclusive + public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + throws SignatureCardException, InterruptedException { + + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); + } + + @Override + @Exclusive + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException { + + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR != keyboxName) { + throw new SignatureCardException("Card does not support key " + keyboxName + "."); + } + if (!"http://www.w3.org/2000/09/xmldsig#rsa-sha1".equals(alg)) { + throw new SignatureCardException("Card does not support algorithm " + alg + "."); + } + + byte[] dst = new byte[] { (byte) 0x04, // number of following + // bytes + (byte) 0x80, // tag for algorithm reference + (byte) 0x02, // algorithm reference + (byte) 0x84, // tag for private key reference + (byte) 0x83 // private key reference + }; + + 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(); + + try { + + CardChannel channel = getCardChannel(); + + // SELECT MF + execSELECT_FID(channel, MF); + // VERIFY + execMSE(channel, 0x41, 0xb6, dst); + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE + verifyPINLoop(channel, SS_PIN_SPEC, provider); + // MANAGE SECURITY ENVIRONMENT : SET DST + return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel, digest); + + } catch (CardException e) { + log.warn(e); + throw new SignatureCardException("Failed to access card.", e); + } + + } + + public String toString() { + return "Belpic Card"; + } + + protected void verifyPINLoop(CardChannel channel, PINSpec spec, + PINProvider provider) throws LockedException, NotActivatedException, + SignatureCardException, InterruptedException, CardException { + + int retries = -1; //verifyPIN(channel, spec, null, -1); + do { + retries = verifyPIN(channel, spec, provider, retries); + } while (retries > 0); + } + + protected int verifyPIN(CardChannel channel, PINSpec pinSpec, + PINProvider provider, int retries) throws SignatureCardException, + LockedException, NotActivatedException, InterruptedException, + CardException { + + VerifyAPDUSpec apduSpec = new VerifyAPDUSpec( + new byte[] { + (byte) 0x00, (byte) 0x20, (byte) 0x00, pinSpec.getKID(), (byte) 0x08, + (byte) 0x20, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff }, + 1, VerifyAPDUSpec.PIN_FORMAT_BCD, 7, 4, 4); + + ResponseAPDU resp = reader.verify(channel, apduSpec, pinSpec, provider, 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); + } + + } + + protected byte[] execSELECT_FID(CardChannel channel, byte[] fid) + throws SignatureCardException, CardException { + + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0xA4, 0x02, 0x0C, fid, 256)); + + if (resp.getSW() == 0x6A82) { + String msg = "File or application not found FID=" + + SMCCHelper.toString(fid) + " SW=" + + Integer.toHexString(resp.getSW()) + "."; + log.info(msg); + throw new FileNotFoundException(msg); + } else if (resp.getSW() != 0x9000) { + String msg = "Failed to select application FID=" + + SMCCHelper.toString(fid) + " SW=" + + Integer.toHexString(resp.getSW()) + "."; + log.error(msg); + throw new SignatureCardException(msg); + } else { + return resp.getBytes(); + } + + } + + protected void execMSE(CardChannel channel, int p1, int p2, byte[] data) + throws CardException, SignatureCardException { + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0x22, p1, p2, data, 256)); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("MSE:SET failed: SW=" + + Integer.toHexString(resp.getSW())); + } + } + + protected byte[] execPSO_COMPUTE_DIGITAL_SIGNATURE(CardChannel channel, byte[] hash) + throws CardException, SignatureCardException { + ResponseAPDU resp; + resp = channel.transmit( + new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, hash, 256)); + if (resp.getSW() == 0x6982) { + throw new SecurityStatusNotSatisfiedException(); + } else if (resp.getSW() == 0x6983) { + throw new LockedException(); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException( + "PSO: COMPUTE DIGITAL SIGNATRE failed: SW=" + + Integer.toHexString(resp.getSW())); + } else { + return resp.getData(); + } + } + + + + +}
\ No newline at end of file diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ITCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ITCard.java new file mode 100644 index 00000000..831a1f9b --- /dev/null +++ b/smcc/src/main/java/at/gv/egiz/smcc/ITCard.java @@ -0,0 +1,297 @@ +/* +* 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.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.smartcardio.CardChannel; +import javax.smartcardio.CardException; +import javax.smartcardio.CommandAPDU; +import javax.smartcardio.ResponseAPDU; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.smcc.util.ISO7816Utils; +import at.gv.egiz.smcc.util.SMCCHelper; + +public class ITCard extends AbstractSignatureCard { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(STARCOSCard.class); + + public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 }; + + public static final byte[] DF1 = new byte[] { (byte) 0x11, (byte) 0x00 }; + + public static final byte[] EF_C_Carta = new byte[] { (byte) 0x11, (byte) 0x01 }; + + private static final PINSpec SS_PIN_SPEC = + new PINSpec(5, 8, "[0-9]", + "at/gv/egiz/smcc/ITCard", "sig.pin", (byte) 0x10, + new byte[] { (byte) 0x11, (byte) 0x00 }); + + /** + * Creates a new instance. + */ + public ITCard() { + super("at/gv/egiz/smcc/ITCard"); + pinSpecs.add(SS_PIN_SPEC); + } + + @Override + @Exclusive + public byte[] getCertificate(KeyboxName keyboxName) + throws SignatureCardException, InterruptedException { + + if (keyboxName != KeyboxName.SECURE_SIGNATURE_KEYPAIR) { + throw new IllegalArgumentException("Keybox " + keyboxName + + " not supported"); + } + + try { + CardChannel channel = getCardChannel(); + // SELECT MF + execSELECT_FID(channel, MF); + // SELECT application + execSELECT_FID(channel, DF1); + // SELECT EF_C_Carta + byte[] fcx = execSELECT_FID(channel, EF_C_Carta); + int maxsize = ISO7816Utils.getLengthFromFCx(fcx); + // READ BINARY + byte[] certificate = ISO7816Utils.readTransparentFileTLV(channel, maxsize, (byte) 0x30); + if (certificate == null) { + throw new NotActivatedException(); + } + return certificate; + } catch (FileNotFoundException e) { + throw new NotActivatedException(); + } catch (CardException e) { + log.info("Failed to get certificate.", e); + throw new SignatureCardException(e); + } + + } + + @Override + @Exclusive + public byte[] getInfobox(String infobox, PINProvider provider, String domainId) + throws SignatureCardException, InterruptedException { + + throw new IllegalArgumentException("Infobox '" + infobox + + "' not supported."); + } + + @Override + @Exclusive + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINProvider provider, String alg) throws SignatureCardException, + InterruptedException, IOException { + + if (KeyboxName.SECURE_SIGNATURE_KEYPAIR != keyboxName) { + throw new SignatureCardException("Card does not support key " + keyboxName + "."); + } + if (!"http://www.w3.org/2000/09/xmldsig#rsa-sha1".equals(alg)) { + throw new SignatureCardException("Card does not support algorithm " + alg + "."); + } + + byte[] dst = new byte[] { + (byte) 0x83, // tag for algorithm reference + (byte) 0x01, // algorithm reference + (byte) 0x01 // private key reference + }; + + 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(); + + try { + + CardChannel channel = getCardChannel(); + + // SELECT MF + execSELECT_FID(channel, MF); + // VERIFY + verifyPINLoop(channel, SS_PIN_SPEC, provider); + // MANAGE SECURITY ENVIRONMENT : RESTORE SE + execMSE(channel, 0xF3, 0x03, null); + // MANAGE SECURITY ENVIRONMENT : SET DST + execMSE(channel, 0xF1, 0xB8, dst); + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE + return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel, digest); + + } catch (CardException e) { + log.warn(e); + throw new SignatureCardException("Failed to access card.", e); + } + + } + + protected void verifyPINLoop(CardChannel channel, PINSpec spec, + PINProvider provider) throws LockedException, NotActivatedException, + SignatureCardException, InterruptedException, CardException { + + int retries = -1; + do { + retries = verifyPIN(channel, spec, provider, retries); + } while (retries >= -1); + } + + protected int verifyPIN(CardChannel channel, PINSpec pinSpec, + PINProvider provider, int retries) throws SignatureCardException, + LockedException, NotActivatedException, InterruptedException, + CardException { + + VerifyAPDUSpec apduSpec = new VerifyAPDUSpec( + new byte[] { + (byte) 0x00, (byte) 0x20, (byte) 0x00, pinSpec.getKID(), (byte) 0x08, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }, + 0, VerifyAPDUSpec.PIN_FORMAT_ASCII, 8); + + ResponseAPDU resp = reader.verify(channel, apduSpec, pinSpec, provider, retries); + + if (resp.getSW() == 0x9000) { + return -2; + } + if (resp.getSW() >> 4 == 0x63c) { + return 0x0f & resp.getSW(); + } + + switch (resp.getSW()) { + case 0x6300: + // incorrect PIN, number of retries not provided + return -1; + case 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); + } + + } + + protected byte[] execSELECT_FID(CardChannel channel, byte[] fid) + throws SignatureCardException, CardException { + + ResponseAPDU resp = channel.transmit( + new CommandAPDU(0x00, 0xA4, 0x00, 0x00, fid, 256)); + + if (resp.getSW() == 0x6A82) { + String msg = "File or application not found FID=" + + SMCCHelper.toString(fid) + " SW=" + + Integer.toHexString(resp.getSW()) + "."; + log.info(msg); + throw new FileNotFoundException(msg); + } else if (resp.getSW() != 0x9000) { + String msg = "Failed to select application FID=" + + SMCCHelper.toString(fid) + " SW=" + + Integer.toHexString(resp.getSW()) + "."; + log.error(msg); + throw new SignatureCardException(msg); + } else { + return resp.getBytes(); + } + + } + + protected void execMSE(CardChannel channel, int p1, int p2, byte[] data) + throws CardException, SignatureCardException { + + ResponseAPDU resp; + if (data == null) { + resp = channel.transmit(new CommandAPDU(0x00, 0x22, p1, p2)); + } else { + resp = channel.transmit(new CommandAPDU(0x00, 0x22, p1, p2, data)); + } + + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("MSE:SET failed: SW=" + + Integer.toHexString(resp.getSW())); + } + } + + protected byte[] execPSO_COMPUTE_DIGITAL_SIGNATURE(CardChannel channel, + byte[] hash) throws CardException, SignatureCardException { + + byte[] oid = new byte[] { (byte) 0x30, (byte) 0x21, (byte) 0x30, + (byte) 0x09, (byte) 0x06, (byte) 0x05, (byte) 0x2b, + (byte) 0x0e, (byte) 0x03, (byte) 0x02, (byte) 0x1a, + (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x14 }; + + ByteArrayOutputStream data = new ByteArrayOutputStream(); + + try { + // header + data.write(new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x01 }); + // padding + for (int i = 0, len = 125 - hash.length - oid.length; i < len; i++) { + data.write((byte) 0xFF); + } + data.write((byte) 0x00); + // oid + data.write(oid); + // hash + data.write(hash); + } catch (IOException e) { + throw new SignatureCardException(e); + } + + ResponseAPDU resp = channel + .transmit(new CommandAPDU(0x00, 0x2A, 0x80, 0x86, data.toByteArray(), 0x81)); + + + if (resp.getSW() == 0x6982) { + throw new SecurityStatusNotSatisfiedException(); + } else if (resp.getSW() == 0x6983) { + throw new LockedException(); + } else if (resp.getSW() != 0x9000) { + throw new SignatureCardException( + "PSO: COMPUTE DIGITAL SIGNATRE failed: SW=" + + Integer.toHexString(resp.getSW())); + } else { + return resp.getData(); + } +} + +} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java index a0c2391d..01de8a77 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -19,11 +19,16 @@ package at.gv.egiz.smcc; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.List; +import javax.smartcardio.Card; import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; @@ -41,6 +46,8 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu private static Log log = LogFactory.getLog(STARCOSCard.class); public static final byte[] MF = new byte[] { (byte) 0x3F, (byte) 0x00 }; + + public static final byte[] EF_VERSION = new byte[] { (byte) 0x00, (byte) 0x32 }; /** * Application ID <em>SV-Personendaten</em>. @@ -106,19 +113,6 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu 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) @@ -133,19 +127,6 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu 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) 0x03, // tag, length (algorithm ID) - (byte) 0x13, (byte) 0x35, (byte) 0x10 // ECDSA - }; - public static final byte KID_PIN_CARD = (byte) 0x01; private static final PINSpec CARD_PIN_SPEC = @@ -155,9 +136,11 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu private static final PINSpec SS_PIN_SPEC = new PINSpec(6, 12, "[0-9]", "at/gv/egiz/smcc/STARCOSCard", "sig.pin", KID_PIN_SS, AID_DF_SS); + + protected double version = 1.1; /** - * Creates an new instance. + * Creates a new instance. */ public STARCOSCard() { super("at/gv/egiz/smcc/STARCOSCard"); @@ -165,6 +148,35 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu pinSpecs.add(SS_PIN_SPEC); } + /* (non-Javadoc) + * @see at.gv.egiz.smcc.AbstractSignatureCard#init(javax.smartcardio.Card, javax.smartcardio.CardTerminal) + */ + @Override + public void init(Card card, CardTerminal cardTerminal) { + super.init(card, cardTerminal); + + // determine application version + CardChannel channel = getCardChannel(); + try { + // SELECT MF + execSELECT_MF(channel); + // SELECT EF_VERSION + execSELECT_FID(channel, EF_VERSION); + // READ BINARY + byte[] ver = ISO7816Utils.readRecord(channel, 1); + if (ver[0] == (byte) 0xa5 && ver[2] == (byte) 0x53) { + version = (0x0F & ver[4]) + (0xF0 & ver[5])/160.0 + (0x0F & ver[5])/100.0; + String generation = (version < 1.2) ? "<= G2" : "G3"; + log.info("e-card version=" + version + " (" + generation + ")"); + } + } catch (CardException e) { + log.warn(e); + } catch (SignatureCardException e) { + log.warn(e); + } + + } + @Override @Exclusive public byte[] getCertificate(KeyboxName keyboxName) @@ -281,19 +293,57 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu @Override @Exclusive - public byte[] createSignature(byte[] hash, KeyboxName keyboxName, - PINProvider provider) throws SignatureCardException, InterruptedException { + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException { - if (hash.length != 20) { - throw new IllegalArgumentException("Hash value must be of length 20."); + ByteArrayOutputStream dst = new ByteArrayOutputStream(); + byte[] ht = null; + + MessageDigest md = null; + try { + if (version < 1.2 && "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1".equals(alg)) { + // local key ID '02' version '00' + dst.write(new byte[] {(byte) 0x84, (byte) 0x03, (byte) 0x80, (byte) 0x02, (byte) 0x00}); + // algorithm ID ECDSA with SHA-1 + dst.write(new byte[] {(byte) 0x89, (byte) 0x03, (byte) 0x13, (byte) 0x35, (byte) 0x10}); + md = MessageDigest.getInstance("SHA-1"); + } else if (version >= 1.2 && "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256".equals(alg)) { + // local key ID '02' version '00' + dst.write(new byte[] {(byte) 0x84, (byte) 0x03, (byte) 0x80, (byte) 0x02, (byte) 0x00}); + // portable algorithm reference + dst.write(new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x04}); + // hash template + ht = new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x40}; + md = MessageDigest.getInstance("SHA256"); + } else if (version >= 1.2 && "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256".equals(alg)) { + // local key ID '03' version '00' + dst.write(new byte[] {(byte) 0x84, (byte) 0x03, (byte) 0x80, (byte) 0x03, (byte) 0x00}); + // portable algorithm reference + dst.write(new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x02}); + // hash template + ht = new byte[] {(byte) 0x80, (byte) 0x01, (byte) 0x40}; + md = MessageDigest.getInstance("SHA256"); + } else { + throw new SignatureCardException("e-card versio " + version + " does not support signature algorithm " + alg + "."); + } + } 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(); + try { CardChannel channel = getCardChannel(); if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - + PINSpec spec = SS_PIN_SPEC; // SELECT MF @@ -303,11 +353,21 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu // VERIFY verifyPINLoop(channel, spec, provider); // MANAGE SECURITY ENVIRONMENT : SET DST - execMSE(channel, 0x41, 0xb6, DST_SS); - // PERFORM SECURITY OPERATION : HASH - execPSO_HASH(channel, hash); - // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE - return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel); + execMSE(channel, 0x41, 0xb6, dst.toByteArray()); + if (ht != null) { + // PERFORM SECURITY OPERATION : SET HT + execMSE(channel, 0x41, 0xaa, ht); + } + if (version < 1.2) { + // PERFORM SECURITY OPERATION : HASH + execPSO_HASH(channel, digest); + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE + return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel, null); + } else { + // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE + return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel, digest); + } + } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { @@ -316,14 +376,17 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu // SELECT application execSELECT_AID(channel, AID_DF_GS); // MANAGE SECURITY ENVIRONMENT : SET DST - execMSE(channel, 0x41, 0xb6, DST_GS); + execMSE(channel, 0x41, 0xb6, dst.toByteArray()); + if (ht != null) { + // PERFORM SECURITY OPERATION : SET HT + execMSE(channel, 0x41, 0xaa, ht); + } // PERFORM SECURITY OPERATION : HASH - execPSO_HASH(channel, hash); - + execPSO_HASH(channel, digest); while (true) { try { // PERFORM SECURITY OPERATION : COMPUTE DIGITAL SIGNATURE - return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel); + return execPSO_COMPUTE_DIGITAL_SIGNATURE(channel, null); } catch (SecurityStatusNotSatisfiedException e) { verifyPINLoop(channel, spec, provider); } @@ -682,7 +745,7 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu ResponseAPDU resp = channel.transmit( new CommandAPDU(0x00, 0x22, p1, p2, data)); if (resp.getSW() != 0x9000) { - throw new SignatureCardException("MSE:SET DST failed: SW=" + throw new SignatureCardException("MSE:SET failed: SW=" + Integer.toHexString(resp.getSW())); } } @@ -701,10 +764,47 @@ public class STARCOSCard extends AbstractSignatureCard implements PINMgmtSignatu } } - protected byte[] execPSO_COMPUTE_DIGITAL_SIGNATURE(CardChannel channel) + protected void execPSO_HASH(CardChannel channel, InputStream input) + throws SignatureCardException, CardException { + ResponseAPDU resp; + int blockSize = 64; + byte[] b = new byte[blockSize]; + try { + ByteArrayOutputStream data = new ByteArrayOutputStream(); + // initialize + data.write((byte) 0x90); + data.write((byte) 0x00); + resp = channel.transmit( + new CommandAPDU(0x10, 0x2A, 0x90, 0xA0, data.toByteArray())); + data.reset(); + for (int l; (l = input.read(b)) != -1;) { + data.write((byte) 0x80); + data.write(l); + data.write(b, 0, l); + resp = channel.transmit( + new CommandAPDU((l == blockSize) ? 0x10 : 0x00, 0x2A, 0x90, 0xA0, data.toByteArray())); + if (resp.getSW() != 0x9000) { + throw new SignatureCardException("PSO:HASH failed: SW=" + + Integer.toHexString(resp.getSW())); + } + data.reset(); + } + } catch (IOException e) { + throw new SignatureCardException(e); + } + + } + + protected byte[] execPSO_COMPUTE_DIGITAL_SIGNATURE(CardChannel channel, byte[] hash) throws CardException, SignatureCardException { - ResponseAPDU resp = channel.transmit( - new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, 256)); + ResponseAPDU resp; + if (hash != null) { + resp = channel.transmit( + new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, hash, 256)); + } else { + resp = channel.transmit( + new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, 256)); + } if (resp.getSW() == 0x6982) { throw new SecurityStatusNotSatisfiedException(); } else if (resp.getSW() == 0x6983) { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java index 279362c0..670704d5 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -20,6 +20,7 @@ import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.security.InvalidKeyException; @@ -308,7 +309,7 @@ public class SWCard implements SignatureCard { } @Override - public byte[] createSignature(byte[] hash, KeyboxName keyboxName, PINProvider provider) throws SignatureCardException, InterruptedException { + public byte[] createSignature(InputStream input, KeyboxName keyboxName, PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException { // KeyStore password char[] password = getPassword(keyboxName); @@ -363,7 +364,10 @@ public class SWCard implements SignatureCard { try { Signature signature = Signature.getInstance(algorithm); signature.initSign(privateKey); - signature.update(hash); + int l; + for (byte[] b = new byte[20]; (l = input.read(b)) != -1;) { + signature.update(b, 0, l); + } return signature.sign(); } catch (NoSuchAlgorithmException e) { String msg = "Algorithm + '" + algorithm + "' not supported for signing."; diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java index 1a163783..3d56f97b 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java @@ -18,6 +18,9 @@ package at.gv.egiz.smcc; import at.gv.egiz.smcc.ccid.CCID; + +import java.io.IOException; +import java.io.InputStream; import java.util.Locale; import javax.smartcardio.Card; @@ -101,15 +104,17 @@ public interface SignatureCard { /** * - * @param hash + * @param input * @param keyboxName * @param provider + * @param alg TODO * @return * @throws at.gv.egiz.smcc.SignatureCardException * @throws java.lang.InterruptedException if applet is destroyed while in pin dialog + * @throws IOException */ - public byte[] createSignature(byte[] hash, KeyboxName keyboxName, - PINProvider provider) throws SignatureCardException, InterruptedException; + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINProvider provider, String alg) throws SignatureCardException, InterruptedException, IOException; public CCID getReader(); 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 26844473..47053f98 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCardFactory.java @@ -170,21 +170,21 @@ public class SignatureCardFactory { }, "at.gv.egiz.smcc.STARCOSCard")); -// // 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) -// new byte[] { -// (byte) 0x3b, (byte) 0xdd, (byte) 0x96, (byte) 0xff, (byte) 0x81, (byte) 0xb1, (byte) 0xfe, (byte) 0x45, -// (byte) 0x1f, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, -// (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 -// }, -// // mask ( -// new byte[] { -// (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, -// (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, -// (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 -// }, -// "at.gv.egiz.smcc.STARCOSCard")); + // 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) + new byte[] { + (byte) 0x3b, (byte) 0xdd, (byte) 0x96, (byte) 0xff, (byte) 0x81, (byte) 0xb1, (byte) 0xfe, (byte) 0x45, + (byte) 0x1f, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 + }, + // mask ( + new byte[] { + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + (byte) 0xff, (byte) 0xff, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 + }, + "at.gv.egiz.smcc.STARCOSCard")); // a-sign premium supportedCards.add(new SupportedCard( @@ -202,6 +202,72 @@ public class SignatureCardFactory { }, "at.gv.egiz.smcc.ACOSCard")); + // BELPIC + supportedCards.add(new SupportedCard( + // ATR (3b:98:13:40:0A:A5:03:01:01:01:AD:13:11) + new byte[] { (byte) 0x3b, (byte) 0x98, (byte) 0x13, + (byte) 0x40, (byte) 0x0a, (byte) 0xa5, (byte) 0x03, + (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0xad, + (byte) 0x13, (byte) 0x11 }, + // mask (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 }, + "at.gv.egiz.smcc.BELPICCard")); + + // ITCards + supportedCards.add(new SupportedCard( + // ATR = + // [3b:ff:18:00:ff:81:31:fe:55:00:6b:02:09:02:00:01:11:01:43:4e:53:11:31:80:8e] + new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18, + (byte) 0x00, (byte) 0xff, (byte) 0x81, (byte) 0x31, + (byte) 0xfe, (byte) 0x55, (byte) 0x00, (byte) 0x6b, + (byte) 0x02, (byte) 0x09 /* + * , (byte) 0x02, (byte) 0x00, + * (byte) 0x01, (byte) 0x11, + * (byte) 0x01, (byte) 0x43, + * (byte) 0x4e, (byte) 0x53, + * (byte) 0x11, (byte) 0x31, + * (byte) 0x80, (byte) 0x8e + */ + }, + // mask (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, + * (byte) 0xff, (byte) 0xff, + * (byte) 0xff, (byte) 0xff + */ + }, "at.gv.egiz.smcc.ITCard")); + supportedCards.add(new SupportedCard( + // ATR + // (3B:FF:18:00:FF:C1:0A:31:FE:55:00:6B:05:08:C8:05:01:01:01:43:4E:53:10:31:80:1C) + new byte[] { (byte) 0x3b, (byte) 0xff, (byte) 0x18, + (byte) 0x00, (byte) 0xFF, (byte) 0xC1, (byte) 0x0a, + (byte) 0x31, (byte) 0xfe, (byte) 0x55, (byte) 0x00, + (byte) 0x6B, (byte) 0x05, (byte) 0x08, (byte) 0xC8, + (byte) 0x05, (byte) 0x01, (byte) 0x01, (byte) 0x01, + (byte) 0x43, (byte) 0x4E, (byte) 0x53, (byte) 0x10, + (byte) 0x31, (byte) 0x80, (byte) 0x1C }, + // mask + // (ff:ff:ff:00:00:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:00:00:00:00) + new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, + (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, (byte) 0xff, (byte) 0xff }, + "at.gv.egiz.smcc.ITCard")); + + + } /** 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 c5c7cbc9..fcd0b876 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 @@ -103,6 +103,14 @@ public class ISO7816Utils { TransparentFileInputStream is = openTransparentFileInputStream(channel, maxSize); + + return readTransparentFileTLV(is, maxSize, expectedType); + + } + + public static byte[] readTransparentFileTLV(TransparentFileInputStream is, int maxSize, + byte expectedType) throws CardException, SignatureCardException { + try { @@ -170,7 +178,8 @@ public class ISO7816Utils { while (pos < (fcx[1] - 2)) { switch (fcx[pos]) { - case (byte) 0x80: { + case (byte) 0x80: + case (byte) 0x81: { len = 0xFF & fcx[pos + 2]; for (int i = 1; i < fcx[pos + 1]; i++) { len<<=8; |