From f6e55fe06d5512afeb1981b37df00a45f2d6d5d3 Mon Sep 17 00:00:00 2001 From: Andreas Fitzek Date: Tue, 18 Nov 2014 12:50:40 +0100 Subject: Gemalto ID .Net extension, latest certificate selection algorithm and key Identifiers 00 - 14 for direct addressing --- .../slcommands/impl/CertificatesInfoboxImpl.java | 17 +- .../java/at/gv/egiz/smcc/GemaltoNetV2_0Card.java | 288 ++++++++++++++++----- 2 files changed, 242 insertions(+), 63 deletions(-) diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CertificatesInfoboxImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CertificatesInfoboxImpl.java index 54697991..4a7a15bb 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CertificatesInfoboxImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CertificatesInfoboxImpl.java @@ -57,7 +57,22 @@ public class CertificatesInfoboxImpl extends AbstractAssocArrayInfobox { */ public static final String[] CERTIFICATES_KEYS = new String[] { "SecureSignatureKeypair", - "CertifiedKeypair" }; + "CertifiedKeypair", + "00", + "01", + "02", + "03", + "04", + "05", + "06", + "07", + "08", + "09", + "10", + "11", + "12", + "13", + "14"}; @Override public String getIdentifier() { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/GemaltoNetV2_0Card.java b/smcc/src/main/java/at/gv/egiz/smcc/GemaltoNetV2_0Card.java index 6a9b0393..d53f52a3 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/GemaltoNetV2_0Card.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/GemaltoNetV2_0Card.java @@ -10,6 +10,8 @@ import java.io.IOException; import java.io.InputStream; import java.nio.CharBuffer; import java.nio.charset.Charset; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.zip.DataFormatException; import java.util.zip.Inflater; @@ -30,28 +32,80 @@ import at.gv.egiz.smcc.util.MSCMService; public class GemaltoNetV2_0Card extends AbstractSignatureCard implements PINMgmtSignatureCard { + private static final String KSC_PATTERN = "ksc(\\p{XDigit}\\p{XDigit})"; + private static final String CERT_FILE = "mscp\\ksc"; + + private static final String CONTAINER_00 = "00"; + private static final String CONTAINER_01 = "01"; + private static final String CONTAINER_02 = "02"; + private static final String CONTAINER_03 = "03"; + private static final String CONTAINER_04 = "04"; + private static final String CONTAINER_05 = "05"; + private static final String CONTAINER_06 = "06"; + private static final String CONTAINER_07 = "07"; + private static final String CONTAINER_08 = "08"; + private static final String CONTAINER_09 = "09"; + private static final String CONTAINER_10 = "10"; + private static final String CONTAINER_11 = "11"; + private static final String CONTAINER_12 = "12"; + private static final String CONTAINER_13 = "13"; + private static final String CONTAINER_14 = "14"; + + private static final KeyboxName KEYBOX_CONTAINER_00 = KeyboxName + .getKeyboxName(CONTAINER_00); + private static final KeyboxName KEYBOX_CONTAINER_01 = KeyboxName + .getKeyboxName(CONTAINER_01); + private static final KeyboxName KEYBOX_CONTAINER_02 = KeyboxName + .getKeyboxName(CONTAINER_02); + private static final KeyboxName KEYBOX_CONTAINER_03 = KeyboxName + .getKeyboxName(CONTAINER_03); + private static final KeyboxName KEYBOX_CONTAINER_04 = KeyboxName + .getKeyboxName(CONTAINER_04); + private static final KeyboxName KEYBOX_CONTAINER_05 = KeyboxName + .getKeyboxName(CONTAINER_05); + private static final KeyboxName KEYBOX_CONTAINER_06 = KeyboxName + .getKeyboxName(CONTAINER_06); + private static final KeyboxName KEYBOX_CONTAINER_07 = KeyboxName + .getKeyboxName(CONTAINER_07); + private static final KeyboxName KEYBOX_CONTAINER_08 = KeyboxName + .getKeyboxName(CONTAINER_08); + private static final KeyboxName KEYBOX_CONTAINER_09 = KeyboxName + .getKeyboxName(CONTAINER_09); + private static final KeyboxName KEYBOX_CONTAINER_10 = KeyboxName + .getKeyboxName(CONTAINER_10); + private static final KeyboxName KEYBOX_CONTAINER_11 = KeyboxName + .getKeyboxName(CONTAINER_11); + private static final KeyboxName KEYBOX_CONTAINER_12 = KeyboxName + .getKeyboxName(CONTAINER_12); + private static final KeyboxName KEYBOX_CONTAINER_13 = KeyboxName + .getKeyboxName(CONTAINER_13); + private static final KeyboxName KEYBOX_CONTAINER_14 = KeyboxName + .getKeyboxName(CONTAINER_14); + private final Logger log = LoggerFactory .getLogger(GemaltoNetV2_0Card.class); PinInfo pinPinInfo; PinInfo pukPinInfo; - + + Pattern pattern; + private final byte[] SHA1_PADDING = 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 }; - + (byte) 0x30, (byte) 0x21, (byte) 0x30, (byte) 0x09, (byte) 0x06, + (byte) 0x05, (byte) 0x2B, (byte) 0x0E, (byte) 0x03, (byte) 0x02, + (byte) 0x1A, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x14 }; + private final byte[] SHA256_PADDING = new byte[] { - (byte) 0x30, (byte) 0x31, (byte) 0x30, (byte) 0x0d, (byte) 0x06, - (byte) 0x09, (byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, - (byte) 0x65, (byte) 0x03, (byte) 0x04, (byte) 0x02, (byte) 0x01, - (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x20 }; + (byte) 0x30, (byte) 0x31, (byte) 0x30, (byte) 0x0d, (byte) 0x06, + (byte) 0x09, (byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, + (byte) 0x65, (byte) 0x03, (byte) 0x04, (byte) 0x02, (byte) 0x01, + (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x20 }; public void init(Card card, CardTerminal cardTerminal) { super.init(card, cardTerminal); - + pattern = Pattern.compile(KSC_PATTERN); log.info("GemaltoNetV2 card found"); pinPinInfo = new PinInfo(4, 64, "[0-9]", @@ -62,15 +116,89 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements "at/gv/egiz/smcc/GemaltoNetV2_0Card", "sig.puk", (byte) 2, new byte[] {}, 3); } - + public static byte[] hexStringToByteArray(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i+1), 16)); - } - return data; + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character + .digit(s.charAt(i + 1), 16)); + } + return data; + } + + private int findKeyIndex() throws IOException, CardException, MSCMException { + MSCMService service = new MSCMService(this.getCardChannel()); + String[] files = service.getFiles("mscp"); + int currentMax = -1; + for (int i = 0; i < files.length; i++) { + String fileName = files[i]; + log.trace("Checking file: " + files[i]); + Matcher matcher = pattern.matcher(fileName); + if (matcher.find()) { + if (matcher.groupCount() == 1) { + String fidx = matcher.group(1); + log.trace("Found idx: " + fidx); + try { + int curr = Integer.parseInt(fidx, 16); + if (currentMax < curr) { + currentMax = curr; + } + } catch (NumberFormatException e) { + log.warn("Matched Regex but is no integer!", e); + } + } + } + } + return currentMax; + } + + private String getCertificateFileFormIdx(int idx) { + return String.format("%s%02x", CERT_FILE, idx); + } + + private int resolveKeyboxIdx(KeyboxName keyboxName) { + if (keyboxName.equals(KEYBOX_CONTAINER_00)) { + return 0; + } else if (keyboxName.equals(KEYBOX_CONTAINER_01)) { + return 1; + } else if (keyboxName.equals(KEYBOX_CONTAINER_02)) { + return 2; + } else if (keyboxName.equals(KEYBOX_CONTAINER_03)) { + return 3; + } else if (keyboxName.equals(KEYBOX_CONTAINER_04)) { + return 4; + } else if (keyboxName.equals(KEYBOX_CONTAINER_05)) { + return 5; + } else if (keyboxName.equals(KEYBOX_CONTAINER_06)) { + return 6; + } else if (keyboxName.equals(KEYBOX_CONTAINER_07)) { + return 7; + } else if (keyboxName.equals(KEYBOX_CONTAINER_08)) { + return 8; + } else if (keyboxName.equals(KEYBOX_CONTAINER_09)) { + return 9; + } else if (keyboxName.equals(KEYBOX_CONTAINER_10)) { + return 10; + } else if (keyboxName.equals(KEYBOX_CONTAINER_11)) { + return 11; + } else if (keyboxName.equals(KEYBOX_CONTAINER_12)) { + return 12; + } else if (keyboxName.equals(KEYBOX_CONTAINER_13)) { + return 13; + } else if (keyboxName.equals(KEYBOX_CONTAINER_14)) { + return 14; + } + return -1; + } + + private String resolveKeybox(KeyboxName keyboxName) { + int idx = resolveKeyboxIdx(keyboxName); + if(idx < 0) { + return null; + } + + return getCertificateFileFormIdx(idx); } @Override @@ -79,7 +207,19 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements try { CardChannel channel = this.getCardChannel();// this.getCard().getBasicChannel(); MSCMService service = new MSCMService(channel); - byte[] filecnt = service.readFile("mscp\\ksc00"); + String certFile = resolveKeybox(keyboxName); + + if(certFile == null) { + int idx = findKeyIndex(); + if(idx < 0) { + throw new SignatureCardException("There is no certificate available on this token!"); + } + certFile = getCertificateFileFormIdx(idx); + } + + log.info("Reading certificate: " + certFile); + + byte[] filecnt = service.readFile(certFile); Inflater inflater = new Inflater(); inflater.setInput(filecnt, 4, filecnt.length - 4); @@ -111,8 +251,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements @Override public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) throws SignatureCardException, InterruptedException { - throw new IllegalArgumentException("Infobox '" + infobox - + "' not supported."); + return getCertificate(KeyboxName.getKeyboxName(infobox), pinGUI); } @Override @@ -122,15 +261,31 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements boolean sha1 = false; MessageDigest md; + int ctrIdx = -1; try { - if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName) - && (alg == null || "http://www.w3.org/2000/09/xmldsig#rsa-sha1" - .equals(alg))) { + + ctrIdx = resolveKeyboxIdx(keyboxName); + + if(ctrIdx < 0) { + ctrIdx = findKeyIndex(); + if(ctrIdx < 0) { + throw new SignatureCardException("There is no private key available on this token!"); + } + } + + if(ctrIdx > 14) { + log.error("Key Index is bigger the maximum container available"); + throw new SignatureCardException("There is no private key available on this token!"); + } + + log.info("Signing with key: " + ctrIdx); + + if (alg == null || "http://www.w3.org/2000/09/xmldsig#rsa-sha1" + .equals(alg)) { md = MessageDigest.getInstance("SHA-1"); sha1 = true; - } else if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName) - && ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" - .equals(alg))) { + } else if ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" + .equals(alg)) { md = MessageDigest.getInstance("SHA-256"); } else { throw new SignatureCardException( @@ -140,6 +295,12 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements } catch (CryptoException e) { log.error("Failed to get MessageDigest.", e); throw new SignatureCardException(e); + } catch (CardException e) { + log.error("Failed to get MessageDigest.", e); + throw new SignatureCardException(e); + } catch (MSCMException e) { + log.error("Failed to get MessageDigest.", e); + throw new SignatureCardException(e); } byte[] digest = new byte[md.getDigestLength()]; @@ -156,11 +317,10 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements MSCMService service = new MSCMService(channel); verifyPINLoop(channel, pinPinInfo, pinGUI); - + ByteArrayOutputStream fdata = new ByteArrayOutputStream(); - - - if(sha1) { + + if (sha1) { fdata.write(SHA1_PADDING); } else { fdata.write(SHA256_PADDING); @@ -169,7 +329,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements fdata.close(); byte[] msg = fdata.toByteArray(); byte[] paded = padding.pad(msg); - byte[] sign = service.privateKeyDecrypt((byte) 0, (byte) 2, paded); + byte[] sign = service.privateKeyDecrypt((byte) ctrIdx, (byte) 2, paded); return sign; } catch (Throwable e) { log.warn("Failed to execute command.", e); @@ -177,35 +337,37 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements } } - protected void unblockPINLoop(CardChannel channel, - ModifyPINGUI provider, PinInfo pin) throws InterruptedException, CardException, SignatureCardException{ + protected void unblockPINLoop(CardChannel channel, ModifyPINGUI provider, + PinInfo pin) throws InterruptedException, CardException, + SignatureCardException { int retries = -1; do { retries = exec_unblockPIN(channel, provider, pin); } while (retries > 0); } - + /* * Unblock PIN with PUK code */ - protected int exec_unblockPIN(CardChannel channel, ModifyPINGUI changePINGUI, PinInfo pin) + protected int exec_unblockPIN(CardChannel channel, + ModifyPINGUI changePINGUI, PinInfo pin) throws InterruptedException, CardException, SignatureCardException { - - + char[] oldPIN = changePINGUI.providePUK(pin, pukPinInfo, pukPinInfo.retries); char[] newPIN = changePINGUI.provideNewPIN(pin); - + byte[] ascii_pin = encodePIN(newPIN); MSCMService service = new MSCMService(channel); - + try { byte[] key = hexStringToByteArray(new String(oldPIN)); - if(key.length != 24) { - throw new SignatureCardException("Invalid ADMIN PIN (not 24 bytes long)!"); + if (key.length != 24) { + throw new SignatureCardException( + "Invalid ADMIN PIN (not 24 bytes long)!"); } byte[] challenge = service.getChallenge(); byte[] response = service.cryptoResponse(challenge, key); @@ -235,7 +397,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements } } - + protected void verifyPINLoop(CardChannel channel, PinInfo spec, PINGUI provider) throws InterruptedException, CardException, SignatureCardException { @@ -256,11 +418,12 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements char[] pin = provider.providePIN(pinInfo, pinInfo.retries); byte[] ascii_pin = hexStringToByteArray(new String(pin)); - - if(ascii_pin.length != 24) { - throw new SignatureCardException("Invalid ADMIN PIN (not 24 bytes long)!"); + + if (ascii_pin.length != 24) { + throw new SignatureCardException( + "Invalid ADMIN PIN (not 24 bytes long)!"); } - + MSCMService service = new MSCMService(channel); try { @@ -283,7 +446,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements throw new SignatureCardException(e); } } - + /* * Verify PIN/PUK entry */ @@ -303,7 +466,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements } catch (MSCMException e) { try { int tries = service.getTriesRemaining(pinInfo.getKID()); - if(tries == 0) { + if (tries == 0) { pinInfo.setBlocked(); throw new LockedException(); } @@ -328,7 +491,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements int retries = -1; do { - if(pin.getKID() == 2) { + if (pin.getKID() == 2) { retries = exec_changePUK(channel, provider, pin); } else { retries = exec_changePIN(channel, provider, pin); @@ -372,11 +535,13 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements try { byte[] key = hexStringToByteArray(new String(oldPIN)); byte[] keynew = hexStringToByteArray(new String(newPIN)); - if(key.length != 24) { - throw new SignatureCardException("Invalid ADMIN PIN (not 24 bytes long)!"); + if (key.length != 24) { + throw new SignatureCardException( + "Invalid ADMIN PIN (not 24 bytes long)!"); } - if(keynew.length != 24) { - throw new SignatureCardException("Invalid ADMIN PIN (not 24 bytes long)!"); + if (keynew.length != 24) { + throw new SignatureCardException( + "Invalid ADMIN PIN (not 24 bytes long)!"); } byte[] challenge = service.getChallenge(); byte[] response = service.cryptoResponse(challenge, key); @@ -392,7 +557,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements log.info(e.getMessage()); try { int tries = service.getTriesRemaining(pin.getKID()); - if(tries == 0) { + if (tries == 0) { pin.setBlocked(); throw new LockedException(); } @@ -412,7 +577,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements } } - + /* * Unblock PIN with PUK code */ @@ -433,8 +598,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements try { // service -> change pin - service.changePIN((byte) 1, ascii_puk, ascii_pin, - pin.maxRetries); + service.changePIN((byte) 1, ascii_puk, ascii_pin, pin.maxRetries); pin.setActive(pin.maxRetries); return -1; } catch (IOException e) { @@ -446,7 +610,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements log.info(e.getMessage()); try { int tries = service.getTriesRemaining(pin.getKID()); - if(tries == 0) { + if (tries == 0) { pin.setBlocked(); throw new LockedException(); } @@ -480,10 +644,10 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements public void verifyPIN(PinInfo pinInfo, PINGUI pinGUI) throws LockedException, NotActivatedException, CancelledException, SignatureCardException, InterruptedException { - + try { CardChannel channel = this.getCardChannel(); - if(pinInfo.getKID() == 2) { + if (pinInfo.getKID() == 2) { verifyPUK(channel, pinInfo, pinGUI, pinInfo.retries); } else { verifyPIN(channel, pinInfo, pinGUI, pinInfo.retries); @@ -519,7 +683,7 @@ public class GemaltoNetV2_0Card extends AbstractSignatureCard implements public void unblockPIN(PinInfo pinInfo, ModifyPINGUI pukGUI) throws CancelledException, SignatureCardException, InterruptedException { - if(pinInfo.getKID() == 2) { + if (pinInfo.getKID() == 2) { throw new SignatureCardException("Unable to unblock PUK"); } else { CardChannel channel = getCardChannel(); -- cgit v1.2.3