/**************************************************************************** * Copyright (C) 2014-2015 ecsec GmbH. * All rights reserved. * Contact: ecsec GmbH (info@ecsec.de) * * This file is part of the Open eCard App. * * GNU General Public License Usage * This file may be used under the terms of the GNU General Public * License version 3.0 as published by the Free Software Foundation * and appearing in the file LICENSE.GPL included in the packaging of * this file. Please review the following information to ensure the * GNU General Public License version 3.0 requirements will be met: * http://www.gnu.org/copyleft/gpl.html. * * Other Usage * Alternatively, this file may be used in accordance with the terms * and conditions contained in a signed written agreement between * you and ecsec GmbH. * ***************************************************************************/ package org.openecard.plugins.pinplugin.gui; import iso.std.iso_iec._24727.tech.schema.ActionType; import iso.std.iso_iec._24727.tech.schema.CardApplicationConnect; import iso.std.iso_iec._24727.tech.schema.CardApplicationConnectResponse; import iso.std.iso_iec._24727.tech.schema.CardApplicationDisconnect; import iso.std.iso_iec._24727.tech.schema.CardApplicationPath; import iso.std.iso_iec._24727.tech.schema.CardApplicationPathResponse; import iso.std.iso_iec._24727.tech.schema.CardApplicationPathType; import iso.std.iso_iec._24727.tech.schema.ConnectionHandleType; import iso.std.iso_iec._24727.tech.schema.ControlIFD; import iso.std.iso_iec._24727.tech.schema.ControlIFDResponse; import iso.std.iso_iec._24727.tech.schema.DIDAuthenticationDataType; import iso.std.iso_iec._24727.tech.schema.DestroyChannel; import iso.std.iso_iec._24727.tech.schema.Disconnect; import iso.std.iso_iec._24727.tech.schema.EstablishChannel; import iso.std.iso_iec._24727.tech.schema.EstablishChannelResponse; import iso.std.iso_iec._24727.tech.schema.PasswordAttributesType; import iso.std.iso_iec._24727.tech.schema.PasswordTypeType; import static iso.std.iso_iec._24727.tech.schema.PasswordTypeType.ASCII_NUMERIC; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; import org.openecard.common.ECardConstants; import org.openecard.common.I18n; import org.openecard.common.WSHelper; import org.openecard.common.anytype.AuthDataMap; import org.openecard.common.anytype.AuthDataResponse; import org.openecard.common.apdu.ResetRetryCounter; import org.openecard.common.apdu.common.CardResponseAPDU; import org.openecard.common.apdu.exception.APDUException; import org.openecard.common.ifd.anytype.PACEInputType; import org.openecard.common.interfaces.Dispatcher; import org.openecard.common.interfaces.DispatcherException; import org.openecard.common.util.ByteUtils; import org.openecard.common.util.StringUtils; import org.openecard.gui.StepResult; import org.openecard.gui.definition.PasswordField; import org.openecard.gui.definition.Step; import org.openecard.gui.definition.Text; import org.openecard.gui.executor.ExecutionResults; import org.openecard.gui.executor.StepAction; import org.openecard.gui.executor.StepActionResult; import org.openecard.gui.executor.StepActionResultStatus; import org.openecard.ifd.scio.IFDException; import org.openecard.ifd.scio.reader.PCSCFeatures; import org.openecard.ifd.scio.reader.PCSCPinModify; import org.openecard.plugins.pinplugin.RecognizedState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Hans-Martin Haase */ public class GenericPINAction extends StepAction { private static final Logger logger = LoggerFactory.getLogger(GenericPINAction.class); private static final String PIN_ID_CAN = "2"; private static final String PIN_ID_PIN = "3"; private static final String PIN_ID_PUK = "4"; private static final String ISO_8859_1 = "ISO-8859-1"; // Translation constants private static final String PUK_SUCCESS = "action.unblockpin.userconsent.pukstep.puk_success"; private static final String CHANGE_SUCCESS = "action.changepin.userconsent.successstep.description"; private static final String ERROR_CARD_REMOVED = "action.error.card.removed"; private static final String ERROR_INTERNAL = "action.error.internal"; private static final String ERROR_NON_MATCHING_PASSWORDS = "action.error.missing_password_match"; private static final String ERROR_TIMEOUT = "action.error.timeout"; private static final String ERROR_TITLE = "action.error.title"; private static final String ERROR_USER_CANCELLATION_OR_CARD_REMOVED = "action.error.user_cancellation"; private static final String SUCCESS_TITLE = "action.success.title"; private static final String ERROR_UNKNOWN = "action.error.unknown"; private final I18n lang = I18n.getTranslation("pinplugin"); private final boolean capturePin; private final Dispatcher dispatcher; private final GenericPINStep gPINStep; private ConnectionHandleType cHandle; private RecognizedState state; private byte[] slotHandle; public GenericPINAction(String stepID, RecognizedState state, ConnectionHandleType cHandle, Dispatcher dispatcher, GenericPINStep gPINStep, boolean capturePin) { super(gPINStep); this.gPINStep = gPINStep; this.capturePin = capturePin; this.cHandle = cHandle; this.dispatcher = dispatcher; this.state = state; slotHandle = cHandle.getSlotHandle(); } @Override public StepActionResult perform(Map<String, ExecutionResults> oldResults, StepResult result) { if (result.isCancelled()) { return new StepActionResult(StepActionResultStatus.CANCEL); } // do not update in case of status resumed, it destroys the the pace channel and there is no disconnect after // the verification of the CAN so the handle stays the same if (state != RecognizedState.PIN_resumed) { try { updateConnectionHandle(); } catch (DispatcherException | InvocationTargetException ex) { logger.error("An internal error occurred while trying to perform an PIN operation.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_INTERNAL))); } } switch (state) { case PIN_activated_RC3: case PIN_activated_RC2: return performPINChange(oldResults); case PIN_suspended: return performResumePIN(oldResults); case PIN_resumed: return performPINChange(oldResults); case PIN_deactivated: // nothing todo here the error message was displayed so just return next. return new StepActionResult(StepActionResultStatus.NEXT); case PIN_blocked: return performUnblockPIN(oldResults); case PUK_blocked: // nothing todo here the error message was displayed so just return next. return new StepActionResult(StepActionResultStatus.NEXT); case UNKNOWN: // nothing todo here the error message was displayed so just return next. return new StepActionResult(StepActionResultStatus.NEXT); } return null; } private EstablishChannelResponse performPACEWithPIN(Map<String, ExecutionResults> oldResults) throws DispatcherException, InvocationTargetException, ParserConfigurationException { DIDAuthenticationDataType paceInput = new DIDAuthenticationDataType(); paceInput.setProtocol(ECardConstants.Protocol.PACE); AuthDataMap tmp = new AuthDataMap(paceInput); AuthDataResponse paceInputMap = tmp.createResponse(paceInput); if (capturePin) { ExecutionResults executionResults = oldResults.get(getStepID()); PasswordField oldPINField = (PasswordField) executionResults.getResult(GenericPINStep.OLD_PIN_FIELD); String oldPINValue = oldPINField.getValue(); if (oldPINValue.length() > 6 && oldPINValue.length() < 5) { // let the user enter the can again, when input verification failed return null; } else { paceInputMap.addElement(PACEInputType.PIN, oldPINValue); } } paceInputMap.addElement(PACEInputType.PIN_ID, PIN_ID_PIN); // perform PACE by EstablishChannelCommand EstablishChannel eChannel = createEstablishChannelStructure(paceInputMap); return (EstablishChannelResponse) dispatcher.deliver(eChannel); } private EstablishChannelResponse performPACEWithCAN(Map<String, ExecutionResults> oldResults) throws DispatcherException, InvocationTargetException, ParserConfigurationException { DIDAuthenticationDataType paceInput = new DIDAuthenticationDataType(); paceInput.setProtocol(ECardConstants.Protocol.PACE); AuthDataMap tmp = new AuthDataMap(paceInput); AuthDataResponse paceInputMap = tmp.createResponse(paceInput); if (capturePin) { ExecutionResults executionResults = oldResults.get(getStepID()); PasswordField canField = (PasswordField) executionResults.getResult(GenericPINStep.CAN_FIELD); String canValue = canField.getValue(); if (canValue.length() != 6) { // let the user enter the can again, when input verification failed return null; } else { paceInputMap.addElement(PACEInputType.PIN, canValue); } } paceInputMap.addElement(PACEInputType.PIN_ID, PIN_ID_CAN); // perform PACE by EstablishChannelCommand EstablishChannel eChannel = createEstablishChannelStructure(paceInputMap); return (EstablishChannelResponse) dispatcher.deliver(eChannel); } private EstablishChannelResponse performPACEWithPUK(Map<String, ExecutionResults> oldResults) throws DispatcherException, InvocationTargetException, ParserConfigurationException { DIDAuthenticationDataType paceInput = new DIDAuthenticationDataType(); paceInput.setProtocol(ECardConstants.Protocol.PACE); AuthDataMap tmp = new AuthDataMap(paceInput); AuthDataResponse paceInputMap = tmp.createResponse(paceInput); if (capturePin) { ExecutionResults executionResults = oldResults.get(getStepID()); PasswordField pukField = (PasswordField) executionResults.getResult(GenericPINStep.PUK_FIELD); String pukValue = pukField.getValue(); if (pukValue.length() != 10) { // let the user enter the pin again, when there is none entered // TODO inform user that something with his input is wrong return null; } else { paceInputMap.addElement(PACEInputType.PIN, pukValue); } } paceInputMap.addElement(PACEInputType.PIN_ID, PIN_ID_PUK); EstablishChannel eChannel = createEstablishChannelStructure(paceInputMap); return (EstablishChannelResponse) dispatcher.deliver(eChannel); } private EstablishChannel createEstablishChannelStructure(AuthDataResponse paceInputMap) { // EstablishChannel EstablishChannel establishChannel = new EstablishChannel(); establishChannel.setSlotHandle(slotHandle); establishChannel.setAuthenticationProtocolData(paceInputMap.getResponse()); establishChannel.getAuthenticationProtocolData().setProtocol(ECardConstants.Protocol.PACE); return establishChannel; } private StepActionResult performPINChange(Map<String, ExecutionResults> oldResults) { String newPINValue = null; String newPINRepeatValue = null; if (capturePin) { try { ExecutionResults executionResults = oldResults.get(getStepID()); PasswordField newPINField = (PasswordField) executionResults.getResult(GenericPINStep.NEW_PIN_FIELD); newPINValue = newPINField.getValue(); PasswordField newPINRepeatField = (PasswordField) executionResults.getResult(GenericPINStep.NEW_PIN_REPEAT_FIELD); newPINRepeatValue = newPINRepeatField.getValue(); byte[] pin1 = newPINValue.getBytes(ISO_8859_1); byte[] pin2 = newPINRepeatValue.getBytes(ISO_8859_1); if (! ByteUtils.compare(pin1, pin2)) { logger.warn("New PIN does not match the value from the confirmation field."); gPINStep.updateState(state); // to reset the text fields return new StepActionResult(StepActionResultStatus.REPEAT); } } catch (UnsupportedEncodingException ex) { logger.error("ISO_8859_1 charset is not support.", ex); gPINStep.updateState(state); // to reset the text fields return new StepActionResult(StepActionResultStatus.REPEAT); } } try { EstablishChannelResponse pinResponse = performPACEWithPIN(oldResults); if (pinResponse == null) { // the entered pin has a wrong format repeat the entering of the data gPINStep.setFailedPINVerify(false); gPINStep.setWrongPINFormat(true); return new StepActionResult(StepActionResultStatus.REPEAT); } if (pinResponse.getResult().getResultMajor().equals(ECardConstants.Major.ERROR)) { switch (pinResponse.getResult().getResultMinor()) { case ECardConstants.Minor.IFD.PASSWORD_ERROR: gPINStep.setFailedPINVerify(true); gPINStep.setWrongPINFormat(false); gPINStep.updateState(RecognizedState.PIN_activated_RC2); state = RecognizedState.PIN_activated_RC2; return new StepActionResult(StepActionResultStatus.REPEAT); case ECardConstants.Minor.IFD.PASSWORD_SUSPENDED: gPINStep.setFailedPINVerify(true); gPINStep.setWrongPINFormat(false); gPINStep.updateState(RecognizedState.PIN_suspended); state = RecognizedState.PIN_suspended; return new StepActionResult(StepActionResultStatus.REPEAT); case ECardConstants.Minor.IFD.PASSWORD_BLOCKED: gPINStep.setFailedPINVerify(true); gPINStep.setWrongPINFormat(false); gPINStep.updateState(RecognizedState.PIN_blocked); state = RecognizedState.PIN_blocked; return new StepActionResult(StepActionResultStatus.REPEAT); default: WSHelper.checkResult(pinResponse); break; } } if (capturePin) { // pace with the old pin was successful now modify the pin if (newPINValue.equals(newPINRepeatValue) && newPINValue.length() == 6) { // no result check necessary everything except a 9000 leads to an APDU exception sendResetRetryCounter(newPINValue.getBytes(ISO_8859_1)); } } else { ControlIFDResponse resp = sendModifyPIN(); evaluateControlIFDResponse(resp); } // PIN modified successfully, proceed with next step return new StepActionResult(StepActionResultStatus.REPEAT, generateSuccessStep(lang.translationForKey(CHANGE_SUCCESS))); } catch (InvocationTargetException | DispatcherException | APDUException | IFDException | ParserConfigurationException ex) { logger.error("An internal error occurred while trying to change the PIN", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_INTERNAL))); } catch (UnsupportedEncodingException ex) { logger.warn("The encoding of the PIN is wrong.", ex); return new StepActionResult(StepActionResultStatus.REPEAT); } catch (WSHelper.WSException ex) { // This is for PIN Pad Readers in case the user pressed the cancel button on the reader. if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.CANCELLATION_BY_USER)) { logger.error("User canceled the authentication manually or removed the card.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_USER_CANCELLATION_OR_CARD_REMOVED))); } // for people which think they have to remove the card in the process if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE)) { logger.error("The SlotHandle was invalid so probably the user removed the card or an reset occurred.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_CARD_REMOVED))); } // for users which forgot to type in something if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.TIMEOUT_ERROR)) { logger.error("The terminal timed out no password was entered.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_TIMEOUT))); } // the verification of the new pin failed if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.PASSWORDS_DONT_MATCH)) { logger.error("The verification of the new PIN failed.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_NON_MATCHING_PASSWORDS))); } // We don't know what happend so just show an general error message logger.error("An unknown error occurred while trying to change the PIN.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_UNKNOWN))); } finally { try { // destroy the pace channel DestroyChannel destChannel = new DestroyChannel(); destChannel.setSlotHandle(slotHandle); dispatcher.deliver(destChannel); // Transaction based communication does not work on java 8 so the PACE channel is not closed after an // EndTransaction call. So do a reset of the card to close the PACE channel. Disconnect disconnect = new Disconnect(); disconnect.setSlotHandle(slotHandle); disconnect.setAction(ActionType.RESET); dispatcher.deliver(disconnect); } catch (DispatcherException | InvocationTargetException ex) { logger.warn("Failed to destroy the PIN pace channel.", ex); } } } private StepActionResult performResumePIN(Map<String, ExecutionResults> oldResults) { try { EstablishChannelResponse canResponse = performPACEWithCAN(oldResults); if (canResponse == null) { gPINStep.setWrongCANFormat(true); gPINStep.setFailedCANVerify(false); gPINStep.updateState(state); // to reset the text fields return new StepActionResult(StepActionResultStatus.REPEAT); } if (canResponse.getResult().getResultMajor().equals(ECardConstants.Major.ERROR)) { if (canResponse.getResult().getResultMinor().equals(ECardConstants.Minor.IFD.AUTHENTICATION_FAILED)) { gPINStep.setWrongCANFormat(false); gPINStep.setFailedCANVerify(true); gPINStep.updateState(state); // to reset the text fields return new StepActionResult(StepActionResultStatus.REPEAT); } else { WSHelper.checkResult(canResponse); } } gPINStep.updateState(RecognizedState.PIN_resumed); state = RecognizedState.PIN_resumed; return new StepActionResult(StepActionResultStatus.REPEAT); } catch (DispatcherException | InvocationTargetException | ParserConfigurationException ex) { logger.error("An internal error occurred while trying to resume the PIN.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_INTERNAL))); } catch (WSHelper.WSException ex) { // This is for PIN Pad Readers in case the user pressed the cancel button on the reader. if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.CANCELLATION_BY_USER)) { logger.error("User canceled the authentication manually or removed the card.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_USER_CANCELLATION_OR_CARD_REMOVED))); } // for people which think they have to remove the card in the process if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE)) { logger.error("The SlotHandle was invalid so probably the user removed the card or an reset occurred."); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_CARD_REMOVED))); } // for users which forgot to type in something if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.TIMEOUT_ERROR)) { logger.error("The terminal timed out no password was entered.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_TIMEOUT))); } logger.error("An unknown error occurred while trying to verify the CAN.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_UNKNOWN))); } } private StepActionResult performUnblockPIN(Map<String, ExecutionResults> oldResults) { try { EstablishChannelResponse pukResponse = performPACEWithPUK(oldResults); if (pukResponse == null) { gPINStep.setWrongPUKFormat(true); gPINStep.setFailedPUKVerify(false); gPINStep.updateState(state); // to reset the text fields return new StepActionResult(StepActionResultStatus.REPEAT); } if (pukResponse.getResult().getResultMajor().equals(ECardConstants.Major.ERROR)) { if (pukResponse.getResult().getResultMinor().equals(ECardConstants.Minor.IFD.AUTHENTICATION_FAILED)) { // i think we should not display the counter //gPINStep.decreasePUKCounter(); gPINStep.setWrongPUKFormat(false); gPINStep.setFailedPUKVerify(true); gPINStep.updateState(state); // to reset the text fields return new StepActionResult(StepActionResultStatus.REPEAT); } else { WSHelper.checkResult(pukResponse); } } // Here no exception is thrown so sent the ResetRetryCounter command ResetRetryCounter resetRetryCounter = new ResetRetryCounter((byte) 0x03); List<byte[]> responses = new ArrayList<>(); responses.add(new byte[] {(byte) 0x90, (byte) 0x00}); responses.add(new byte[] {(byte) 0x69, (byte) 0x84}); CardResponseAPDU resetCounterResponse = resetRetryCounter.transmit(dispatcher, slotHandle, responses); if (Arrays.equals(resetCounterResponse.getTrailer(), new byte[] {(byte) 0x69, (byte) 0x84})) { gPINStep.updateState(RecognizedState.PUK_blocked); return new StepActionResult(StepActionResultStatus.REPEAT); } else if (Arrays.equals(resetCounterResponse.getTrailer(), new byte[] {(byte) 0x90, (byte) 0x00})) { gPINStep.updateState(RecognizedState.PIN_activated_RC3); return new StepActionResult(StepActionResultStatus.REPEAT, generateSuccessStep(lang.translationForKey(PUK_SUCCESS))); } else { gPINStep.updateState(RecognizedState.UNKNOWN); return new StepActionResult(StepActionResultStatus.REPEAT); } } catch (DispatcherException | InvocationTargetException | APDUException | ParserConfigurationException ex) { logger.error("An internal error occurred while trying to unblock the PIN.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_INTERNAL))); } catch (WSHelper.WSException ex) { // This is for PIN Pad Readers in case the user pressed the cancel button on the reader. if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.CANCELLATION_BY_USER)) { logger.error("User canceled the authentication manually or removed the card.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_USER_CANCELLATION_OR_CARD_REMOVED))); } // for users which forgot to type in something if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.TIMEOUT_ERROR)) { logger.error("The terminal timed out no password was entered.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_TIMEOUT))); } // for people which think they have to remove the card in the process if (ex.getResultMinor().equals(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE)) { logger.error("The SlotHandle was invalid so probably the user removed the card or an reset occurred.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_CARD_REMOVED))); } // We don't know what happend so just show an general error message logger.error("An unknown error occurred while trying to verify the PUK.", ex); return new StepActionResult(StepActionResultStatus.REPEAT, generateErrorStep(lang.translationForKey(ERROR_UNKNOWN))); } finally { try { // destroy the pace channel DestroyChannel destChannel = new DestroyChannel(); destChannel.setSlotHandle(slotHandle); dispatcher.deliver(destChannel); // For readers which do not support DestroyChannel but have generic pace support Disconnect disconnect = new Disconnect(); disconnect.setSlotHandle(slotHandle); disconnect.setAction(ActionType.RESET); dispatcher.deliver(disconnect); } catch (DispatcherException | InvocationTargetException ex) { logger.warn("Failed to destroy the PUK pace channel.", ex); } } } /** * Send a ModifyPIN-PCSC-Command to the Terminal. * * @throws IFDException If building the Command fails. * @throws InvocationTargetException If the ControlIFD command fails. * @throws DispatcherException If an error in the dispatcher occurs. */ private ControlIFDResponse sendModifyPIN() throws IFDException, InvocationTargetException, DispatcherException { PasswordAttributesType pwdAttr = create(true, ASCII_NUMERIC, 6, 6, 6); pwdAttr.setPadChar(new byte[] { (byte) 0x3F }); PCSCPinModify ctrlStruct = new PCSCPinModify(pwdAttr, StringUtils.toByteArray("002C0203")); byte[] structData = ctrlStruct.toBytes(); ControlIFD controlIFD = new ControlIFD(); controlIFD.setCommand(ByteUtils.concatenate((byte) PCSCFeatures.MODIFY_PIN_DIRECT, structData)); controlIFD.setSlotHandle(slotHandle); return (ControlIFDResponse) dispatcher.deliver(controlIFD); } /** * Send a ResetRetryCounter-APDU. * * @throws APDUException if the RRC-APDU could not be sent successfully */ private CardResponseAPDU sendResetRetryCounter(byte[] newPIN) throws APDUException { ResetRetryCounter apdu = new ResetRetryCounter(newPIN, (byte) 0x03); return apdu.transmit(dispatcher, cHandle.getSlotHandle()); } private static PasswordAttributesType create(boolean needsPadding, PasswordTypeType pwdType, int minLen, int storedLen, int maxLen) { PasswordAttributesType r = new PasswordAttributesType(); r.setMinLength(BigInteger.valueOf(minLen)); r.setStoredLength(BigInteger.valueOf(storedLen)); r.setPwdType(pwdType); if (needsPadding) { r.getPwdFlags().add("needs-padding"); } r.setMaxLength(BigInteger.valueOf(maxLen)); return r; } private Step generateSuccessStep(String successMessage) { Step successStep = new Step(lang.translationForKey(SUCCESS_TITLE)); successStep.setReversible(false); Text successText = new Text(successMessage); successStep.getInputInfoUnits().add(successText); return successStep; } private Step generateErrorStep(String errorMessage) { Step errorStep = new Step(lang.translationForKey(ERROR_TITLE)); errorStep.setReversible(false); Text errorText = new Text(errorMessage); errorStep.getInputInfoUnits().add(errorText); return errorStep; } private void evaluateControlIFDResponse(ControlIFDResponse response) throws WSHelper.WSException { byte[] resp = response.getResponse(); switch(ByteUtils.toInteger(resp)) { case 0x64A1: response.setResult(WSHelper.makeResultError(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, "Card was removed.")); break; case 0x6402: response.setResult(WSHelper.makeResultError(ECardConstants.Minor.IFD.PASSWORDS_DONT_MATCH, "The entered passwords do not match.")); break; case 0x6401: response.setResult(WSHelper.makeResultError(ECardConstants.Minor.IFD.CANCELLATION_BY_USER, "The user aborted the password entry.")); break; } WSHelper.checkResult(response); } /** * Update the connection handle. * * This is necessary after every step because we Disconnect the card with a reset if we have success or not. * * @throws DispatcherException * @throws InvocationTargetException */ private void updateConnectionHandle() throws DispatcherException, InvocationTargetException { CardApplicationPath cPath = new CardApplicationPath(); CardApplicationPathType cPathType = new CardApplicationPathType(); cPath.setCardAppPathRequest(cPathType); CardApplicationPathResponse cPathResp = (CardApplicationPathResponse) dispatcher.deliver(cPath); List<CardApplicationPathType> cRes = cPathResp.getCardAppPathResultSet().getCardApplicationPathResult(); for (CardApplicationPathType capt : cRes) { CardApplicationConnect cConn = new CardApplicationConnect(); cConn.setCardApplicationPath(capt); CardApplicationConnectResponse conRes = (CardApplicationConnectResponse) dispatcher.deliver(cConn); String cardType = conRes.getConnectionHandle().getRecognitionInfo().getCardType(); ConnectionHandleType cHandleNew = conRes.getConnectionHandle(); if (cardType.equals("http://bsi.bund.de/cif/npa.xml")) { // ensure same terminal and get the new slothandle if (cHandleNew.getIFDName().equals(cHandle.getIFDName()) && ! Arrays.equals(cHandleNew.getSlotHandle(), slotHandle)) { cHandle = cHandleNew; slotHandle = cHandle.getSlotHandle(); break; // also end if the connection handle found as before than it is still valid } else if (cHandleNew.getIFDName().equals(cHandle.getIFDName()) && Arrays.equals(cHandleNew.getSlotHandle(), slotHandle)) { break; } } else { try { CardApplicationDisconnect disconnect = new CardApplicationDisconnect(); disconnect.setConnectionHandle(conRes.getConnectionHandle()); disconnect.setAction(ActionType.RESET); dispatcher.deliver(disconnect); } catch (DispatcherException | InvocationTargetException ex) { logger.warn("Failed to disconnect card in terminal:" + conRes.getConnectionHandle().getIFDName(), ex); } } } } }