summaryrefslogtreecommitdiff
path: root/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java
diff options
context:
space:
mode:
authortzefferer <tzefferer@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4>2010-11-29 10:02:31 +0000
committertzefferer <tzefferer@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4>2010-11-29 10:02:31 +0000
commit50512519e5dea2405ceaead5ad111e3e827888b2 (patch)
treed6fae2aeee46547130a5c33b065d735d48413070 /smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java
parent41246b466f815e9617a40e2daba7cf4c1f327f97 (diff)
downloadmocca-50512519e5dea2405ceaead5ad111e3e827888b2.tar.gz
mocca-50512519e5dea2405ceaead5ad111e3e827888b2.tar.bz2
mocca-50512519e5dea2405ceaead5ad111e3e827888b2.zip
Bugfix DNIE
FINEID Support git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@848 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java364
1 files changed, 364 insertions, 0 deletions
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java
new file mode 100644
index 00000000..96c8e517
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/FINEIDCard.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2008 Federal Chancellery Austria and
+ * Graz University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package at.gv.egiz.smcc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.smcc.pin.gui.PINGUI;
+import at.gv.egiz.smcc.util.ISO7816Utils;
+import at.gv.egiz.smcc.util.SMCCHelper;
+import at.gv.egiz.smcc.util.TLVSequence;
+
+public class FINEIDCard extends AbstractSignatureCard implements SignatureCard {
+
+ private static final int EF_OD_PADDING = 0xFF;
+ private static final String SIG_CERT_LABEL = "allekirjoitusvarmenne";
+ private static final String SIG_KEY_LABEL = "allekirjoitusavain";
+
+ private final Logger log = LoggerFactory.getLogger(FINEIDCard.class);
+
+ protected PinInfo pinInfo = new PinInfo(6, 8, "[0-9]",
+ "at/gv/egiz/smcc/FINEIDCard", "sig.pin", (byte) 0x00,
+ new byte[] {}, PinInfo.UNKNOWN_RETRIES);
+
+ @Override
+ public byte[] createSignature(InputStream input, KeyboxName keyboxName,
+ PINGUI pinGUI, String alg) throws SignatureCardException,
+ InterruptedException, IOException {
+
+ CardChannel channel = getCardChannel();
+
+ try {
+
+ FINEIDEFObjectDirectory ef_od = new FINEIDEFObjectDirectory(
+ EF_OD_PADDING);
+ ef_od.selectAndRead(channel);
+
+ // read PRKD to find correct key
+ FINEIDCIOKeyDirectory ef_prkd = new FINEIDCIOKeyDirectory(ef_od
+ .getEf_prkd());
+ ef_prkd.selectAndRead(channel);
+
+ byte[] efKey = null;
+ byte[] authID = null;
+
+ for (CIOCertificate cioCertificate : ef_prkd.getCIOs()) {
+ String label = cioCertificate.getLabel();
+ if (label != null
+ && label.toLowerCase().contains(
+ SIG_KEY_LABEL.toLowerCase())) {
+
+ efKey = cioCertificate.getEfidOrPath();
+ authID = cioCertificate.getAuthId();
+ }
+ }
+
+ if (efKey == null) {
+
+ throw new SignatureCardException(
+ "Could not determine path to private key from PrKD.");
+ }
+
+ if (authID == null) {
+
+ throw new SignatureCardException(
+ "Could not determine authID of private key from PrKD.");
+ }
+
+ // read AOD to find associated PIN (authId must match)
+ FINEIDAODirectory ef_aod = new FINEIDAODirectory(ef_od.getEf_aod());
+ ef_aod.selectAndRead(channel);
+
+ byte[] pinPath = null;
+ byte[] pwdRef = null;
+ for (FINEIDAuthenticationObject ao : ef_aod.getAOs()) {
+
+ byte[] id = ao.getAuthId();
+ if (id != null && Arrays.equals(id, authID)) {
+ pinPath = ao.getPath();
+ pwdRef = ao.getPwdReference();
+ }
+ }
+
+ if (pinPath == null) {
+
+ throw new SignatureCardException(
+ "Could not determine path to PIN from AOD.");
+ }
+
+ if (pwdRef == null) {
+
+ throw new SignatureCardException(
+ "Could not determine PIN reference from AOD.");
+ }
+
+ // verify PIN
+ verifyPINLoop(channel, pinInfo, pinGUI, pinPath,
+ pwdRef[pwdRef.length - 1]);
+
+ // Set MSE
+ CommandAPDU selectKeyPath = new CommandAPDU((byte) 0x00,
+ (byte) 0xA4, (byte) 0x08, (byte) 0x00, FINEIDUtil
+ .removeMFPath(efKey));
+ ResponseAPDU resp = channel.transmit(selectKeyPath);
+
+ if (resp.getSW() != 0x9000) {
+
+ throw new SignatureCardException(
+ "Could not select private key file DF.");
+ }
+
+ executeRestoreMSE(channel);
+
+ byte[] dst = new byte[] { (byte) 0x80, (byte) 0x01, (byte) 0x12,
+ (byte) 0x81, (byte) 0x02, efKey[efKey.length - 2],
+ efKey[efKey.length - 1] };
+
+ executeSetMSE(channel, dst);
+
+ // SIGN
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Failed to get MessageDigest.", e);
+ throw new SignatureCardException(e);
+ }
+ // calculate message digest
+ byte[] digest = new byte[md.getDigestLength()];
+ for (int l; (l = input.read(digest)) != -1;) {
+ md.update(digest, 0, l);
+ }
+ digest = md.digest();
+
+ byte[] sigVal = executeSign(channel, digest);
+ return sigVal;
+
+ } catch (CardException e) {
+
+ throw new SignatureCardException("Error creating signature.", e);
+ }
+
+ }
+
+ @Override
+ public byte[] getCertificate(KeyboxName keyboxName, PINGUI pinGUI)
+ throws SignatureCardException, InterruptedException {
+
+ CardChannel channel = getCardChannel();
+
+ try {
+ FINEIDEFObjectDirectory ef_od = new FINEIDEFObjectDirectory(
+ EF_OD_PADDING);
+ ef_od.selectAndRead(channel);
+
+ byte[] certPath = null;
+
+ for (int i = 0; i < ef_od.getEf_cd_list().size(); i++) {
+
+ FINEIDCIOCertificateDirectory ef_cd = new FINEIDCIOCertificateDirectory(
+ ef_od.getEf_cd_list().get(i));
+
+ try {
+ ef_cd.selectAndRead(channel);
+ } catch (IOException e) {
+ log.debug("Cannot read EF.CD - try next one in list..");
+ continue;
+ }
+
+ for (CIOCertificate cioCertificate : ef_cd.getCIOs()) {
+ String label = cioCertificate.getLabel();
+ if (label != null
+ && label.toLowerCase().contains(
+ SIG_CERT_LABEL.toLowerCase())) {
+ certPath = cioCertificate.getEfidOrPath();
+ }
+ }
+ }
+
+ if (certPath == null) {
+
+ throw new SignatureCardException(
+ "Could not determine path to certificate.");
+ }
+
+ log
+ .debug("Read certificate path: "
+ + SMCCHelper.toString(certPath));
+
+ certPath = FINEIDUtil.removeMFPath(certPath);
+
+ CommandAPDU apdu = new CommandAPDU((byte) 0x00, (byte) 0xA4,
+ (byte) 0x08, (byte) 0x00, certPath);
+ ResponseAPDU resp = channel.transmit(apdu);
+
+ byte[] fcx = new TLVSequence(resp.getBytes())
+ .getValue(ISO7816Utils.TAG_FCI);
+ byte[] fileDataLength = new TLVSequence(fcx).getValue(0x81);
+
+ return ISO7816Utils.readTransparentFile(channel,
+ computeLengthFromByteArray(fileDataLength));
+
+ } catch (CardException e) {
+
+ throw new SignatureCardException(
+ "Error reading certificate from card.", e);
+ }
+ }
+
+ @Override
+ public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId)
+ throws SignatureCardException, InterruptedException {
+
+ throw new IllegalArgumentException("Infobox '" + infobox
+ + "' not supported.");
+ }
+
+ protected void verifyPINLoop(CardChannel channel, PinInfo spec,
+ PINGUI provider, byte[] pinPath, byte keyID)
+ throws LockedException, NotActivatedException,
+ SignatureCardException, InterruptedException, CardException {
+
+ CommandAPDU verifySelect = new CommandAPDU((byte) 0x00, (byte) 0xA4,
+ (byte) 0x08, (byte) 0x00, FINEIDUtil.removeMFPath(pinPath));
+ ResponseAPDU response = channel.transmit(verifySelect);
+
+ if (response.getSW() != 0x9000) {
+
+ throw new SignatureCardException("Cannot select PIN path "
+ + SMCCHelper.toString(pinPath) + ": "
+ + Integer.toHexString(response.getSW()));
+ }
+
+ int retries = -1;
+
+ do {
+ retries = verifyPIN(channel, spec, provider, retries, keyID);
+ } while (retries > 0);
+ }
+
+ protected int verifyPIN(CardChannel channel, PinInfo pinSpec,
+ PINGUI provider, int retries, byte keyID)
+ throws SignatureCardException, LockedException,
+ NotActivatedException, InterruptedException, CardException {
+
+ VerifyAPDUSpec apduSpec = new VerifyAPDUSpec(new byte[] { (byte) 0x00,
+ (byte) 0x20, (byte) 0x00, keyID, (byte) 0x08, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00 }, 0,
+ VerifyAPDUSpec.PIN_FORMAT_ASCII, 8);
+
+ ResponseAPDU resp = reader.verify(channel, apduSpec, provider, pinSpec,
+ retries);
+
+ if (resp.getSW() == 0x9000) {
+ return -1;
+ }
+ if (resp.getSW() >> 4 == 0x63c) {
+ return 0x0f & resp.getSW();
+ }
+
+ switch (resp.getSW()) {
+ case 0x6983:
+ // authentication method blocked
+ throw new LockedException();
+ case 0x6984:
+ // reference data not usable
+ throw new NotActivatedException();
+ case 0x6985:
+ // conditions of use not satisfied
+ throw new NotActivatedException();
+
+ default:
+ String msg = "VERIFY failed. SW="
+ + Integer.toHexString(resp.getSW());
+ log.info(msg);
+ throw new SignatureCardException(msg);
+ }
+
+ }
+
+ private void executeRestoreMSE(CardChannel channel) throws CardException {
+
+ CommandAPDU mseRestore = new CommandAPDU((byte) 0x00, (byte) 0x22,
+ (byte) 0xF3, (byte) 0x00);
+ ResponseAPDU resp = channel.transmit(mseRestore);
+
+ if (resp.getSW() != 0x9000) {
+
+ throw new CardException("Error restoring MSE: "
+ + Integer.toHexString(resp.getSW()));
+ }
+
+ }
+
+ private void executeSetMSE(CardChannel channel, byte[] dst)
+ throws CardException {
+
+ CommandAPDU mseSet = new CommandAPDU((byte) 0x00, (byte) 0x22,
+ (byte) 0x41, (byte) 0xB6, dst);
+ ResponseAPDU resp = channel.transmit(mseSet);
+
+ if (resp.getSW() != 0x9000) {
+
+ throw new CardException("Error setting MSE: "
+ + Integer.toHexString(resp.getSW()));
+ }
+
+ }
+
+ private byte[] executeSign(CardChannel channel, byte[] hash)
+ throws CardException {
+
+ CommandAPDU sign = new CommandAPDU((byte) 0x00, (byte) 0x2A,
+ (byte) 0x9E, (byte) 0x9A, hash);
+ ResponseAPDU resp = channel.transmit(sign);
+
+ if (resp.getSW() != 0x9000) {
+
+ throw new CardException("Error signing hash: "
+ + Integer.toHexString(resp.getSW()));
+ }
+
+ return resp.getData();
+ }
+
+ private int computeLengthFromByteArray(byte[] input) {
+
+ int result = 0;
+
+ for (int i = 0; i < input.length; i++) {
+ int current = input[input.length - 1 - i];
+ result = result + (int) (current * Math.pow(256, i));
+ }
+ return result;
+ }
+
+}