extract creation of CommandAPDUs into factory

This commit is contained in:
Vincent Breitmoser
2017-10-23 18:05:15 +02:00
parent 46b69d45c4
commit 8e9a62070d
3 changed files with 178 additions and 56 deletions

View File

@@ -0,0 +1,140 @@
package org.sufficientlysecure.keychain.securitytoken;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;
import android.support.annotation.NonNull;
import javax.smartcardio.CommandAPDU;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
class CommandAPDUFactory {
private static final int MAX_APDU_NC = 255;
private static final int MAX_APDU_NC_EXT = 65535;
private static final int MAX_APDU_NE = 256;
private static final int MAX_APDU_NE_EXT = 65536;
final int CLA = 0x00;
final int MASK_CLA_CHAINING = 1 << 4;
@NonNull
CommandAPDU createPutKeyCommand(byte[] keyBytes) {
return new CommandAPDU(CLA, 0xDB, 0x3F, 0xFF, keyBytes);
}
@NonNull
CommandAPDU createComputeDigitalSignatureCommand(byte[] data) {
return new CommandAPDU(CLA, 0x2A, 0x9E, 0x9A, data, MAX_APDU_NE_EXT);
}
@NonNull
CommandAPDU createPutDataCommand(int dataObject, byte[] data) {
return new CommandAPDU(CLA, 0xDA, (dataObject & 0xFF00) >> 8, dataObject & 0xFF, data);
}
@NonNull
CommandAPDU createDecipherCommand(byte[] data) {
return new CommandAPDU(CLA, 0x2A, 0x80, 0x86, data, MAX_APDU_NE_EXT);
}
@NonNull
CommandAPDU createChangeReferenceDataCommand(int pw, byte[] newPin, byte[] pin) {
return new CommandAPDU(CLA, 0x24, 0x00, pw, Arrays.concatenate(pin, newPin));
}
@NonNull
CommandAPDU createResetRetryCounter(byte[] newPin) {
return new CommandAPDU(CLA, 0x2C, 0x02, 0x81, newPin);
}
@NonNull
CommandAPDU createGetDataCommand(int p1, int p2) {
return new CommandAPDU(CLA, 0xCA, p1, p2, MAX_APDU_NE_EXT);
}
@NonNull
CommandAPDU createGenerateKeyCommand(int slot) {
return new CommandAPDU(CLA, 0x47, 0x80, 0x00, new byte[] { (byte) slot, 0x00 }, MAX_APDU_NE_EXT);
}
@NonNull
CommandAPDU createGetResponseCommand(int lastResponseSw2) {
return new CommandAPDU(CLA, 0xC0, 0x00, 0x00, lastResponseSw2);
}
@NonNull
CommandAPDU createVerifyCommand(int mode, byte[] pin) {
return new CommandAPDU(CLA, 0x20, 0x00, mode, pin);
}
@NonNull
CommandAPDU createSelectFileCommand(String fileAid) {
return new CommandAPDU(CLA, 0xA4, 0x04, 0x00, Hex.decode(fileAid));
}
@NonNull
CommandAPDU createReactivate2Command() {
return new CommandAPDU(CLA, 0x44, 0x00, 0x00);
}
@NonNull
CommandAPDU createReactivate1Command() {
return new CommandAPDU(CLA, 0xE6, 0x00, 0x00);
}
@NonNull
CommandAPDU createInternalAuthenticateCommand(ByteArrayOutputStream pkout) {
return new CommandAPDU(0, 0x88, 0x01, 0x0, pkout.toByteArray(), MAX_APDU_NE_EXT);
}
@NonNull
CommandAPDU createRetrievePublicKeyCommand(byte[] msgCert) {
return new CommandAPDU(0, 0x47, 0x81, 0x00, msgCert, MAX_APDU_NE_EXT);
}
@NonNull
CommandAPDU createRetrieveCertificateCommand() {
return new CommandAPDU(0, 0xA5, 0x03, 0x04,
new byte[]{0x60, 0x04, 0x5C, 0x02, 0x7F, 0x21});
}
@NonNull
CommandAPDU createShortApdu(CommandAPDU apdu) {
int ne = Math.min(apdu.getNe(), MAX_APDU_NE);
return new CommandAPDU(apdu.getCLA(), apdu.getINS(), apdu.getP1(), apdu.getP2(), apdu.getData(), ne);
}
@NonNull
List<CommandAPDU> createChainedApdus(CommandAPDU apdu) {
ArrayList<CommandAPDU> result = new ArrayList<>();
int offset = 0;
byte[] data = apdu.getData();
int ne = Math.min(apdu.getNe(), MAX_APDU_NE);
while (offset < data.length) {
int curLen = Math.min(MAX_APDU_NC, data.length - offset);
boolean last = offset + curLen >= data.length;
int cla = apdu.getCLA() + (last ? 0 : MASK_CLA_CHAINING);
CommandAPDU cmd =
new CommandAPDU(cla, apdu.getINS(), apdu.getP1(), apdu.getP2(), data, offset, curLen, ne);
result.add(cmd);
offset += curLen;
}
return result;
}
boolean isSuitableForShortApdu(CommandAPDU apdu) {
return apdu.getData().length <= MAX_APDU_NC;
}
}

View File

@@ -275,7 +275,7 @@ class SCP11bSecureMessaging implements SecureMessaging {
} }
public static void establish(final SecurityTokenConnection t, final Context ctx) public static void establish(final SecurityTokenConnection t, final Context ctx, CommandAPDUFactory commandFactory)
throws SecureMessagingException, IOException { throws SecureMessagingException, IOException {
CommandAPDU cmd; CommandAPDU cmd;
@@ -285,8 +285,7 @@ class SCP11bSecureMessaging implements SecureMessaging {
t.clearSecureMessaging(); t.clearSecureMessaging();
// retrieving key algorithm // retrieving key algorithm
cmd = new CommandAPDU(0, (byte)0xCA, (byte)0x00, cmd = commandFactory.createGetDataCommand(0x00, OPENPGP_SECURE_MESSAGING_KEY_ATTRIBUTES_TAG);
OPENPGP_SECURE_MESSAGING_KEY_ATTRIBUTES_TAG, SecurityTokenConnection.MAX_APDU_NE_EXT);
resp = t.communicate(cmd); resp = t.communicate(cmd);
if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) { if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) {
throw new SecureMessagingException("failed to retrieve secure messaging key attributes"); throw new SecureMessagingException("failed to retrieve secure messaging key attributes");
@@ -317,13 +316,12 @@ class SCP11bSecureMessaging implements SecureMessaging {
if (prefs != null && prefs.getExperimentalSmartPGPAuthoritiesEnable()) { if (prefs != null && prefs.getExperimentalSmartPGPAuthoritiesEnable()) {
// retrieving certificate // retrieving certificate
cmd = new CommandAPDU(0, (byte) 0xA5, (byte) 0x03, (byte) 0x04, cmd = commandFactory.createRetrieveCertificateCommand();
new byte[]{(byte) 0x60, (byte) 0x04, (byte) 0x5C, (byte) 0x02, (byte) 0x7F, (byte) 0x21});
resp = t.communicate(cmd); resp = t.communicate(cmd);
if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) { if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) {
throw new SecureMessagingException("failed to select secure messaging certificate"); throw new SecureMessagingException("failed to select secure messaging certificate");
} }
cmd = new CommandAPDU(0, (byte) 0xCA, (byte) 0x7F, (byte) 0x21, SecurityTokenConnection.MAX_APDU_NE_EXT); cmd = commandFactory.createGetDataCommand(0x7F, 0x21);
resp = t.communicate(cmd); resp = t.communicate(cmd);
if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) { if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) {
throw new SecureMessagingException("failed to retrieve secure messaging certificate"); throw new SecureMessagingException("failed to retrieve secure messaging certificate");
@@ -333,8 +331,7 @@ class SCP11bSecureMessaging implements SecureMessaging {
} else { } else {
// retrieving public key // retrieving public key
cmd = new CommandAPDU(0, (byte) 0x47, (byte) 0x81, (byte) 0x00, cmd = commandFactory.createRetrievePublicKeyCommand(OPENPGP_SECURE_MESSAGING_KEY_CRT);
OPENPGP_SECURE_MESSAGING_KEY_CRT, SecurityTokenConnection.MAX_APDU_NE_EXT);
resp = t.communicate(cmd); resp = t.communicate(cmd);
if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) { if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) {
throw new SecureMessagingException("failed to retrieve secure messaging public key"); throw new SecureMessagingException("failed to retrieve secure messaging public key");
@@ -395,8 +392,7 @@ class SCP11bSecureMessaging implements SecureMessaging {
pkout = bout; pkout = bout;
// internal authenticate // internal authenticate
cmd = new CommandAPDU(0, (byte)0x88, (byte)0x01, (byte)0x0, pkout.toByteArray(), cmd = commandFactory.createInternalAuthenticateCommand(pkout);
SecurityTokenConnection.MAX_APDU_NE_EXT);
resp = t.communicate(cmd); resp = t.communicate(cmd);
if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) { if (resp.getSW() != SecurityTokenConnection.APDU_SW_SUCCESS) {
throw new SecureMessagingException("failed to initiate internal authenticate"); throw new SecureMessagingException("failed to initiate internal authenticate");

View File

@@ -64,6 +64,8 @@ import java.security.NoSuchAlgorithmException;
import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey; import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPrivateCrtKey;
import java.util.List;
/** /**
* This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant
@@ -71,17 +73,9 @@ import java.security.interfaces.RSAPrivateCrtKey;
* For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf * For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf
*/ */
public class SecurityTokenConnection { public class SecurityTokenConnection {
private static final int MAX_APDU_NC = 255;
private static final int MAX_APDU_NC_EXT = 65535;
private static final int MAX_APDU_NE = 256;
static final int MAX_APDU_NE_EXT = 65536;
static final int APDU_SW_SUCCESS = 0x9000; static final int APDU_SW_SUCCESS = 0x9000;
private static final int APDU_SW1_RESPONSE_AVAILABLE = 0x61; private static final int APDU_SW1_RESPONSE_AVAILABLE = 0x61;
private static final int MASK_CLA_CHAINING = 1 << 4;
// Fidesmo constants // Fidesmo constants
private static final String FIDESMO_APPS_AID_PREFIX = "A000000617"; private static final String FIDESMO_APPS_AID_PREFIX = "A000000617";
@@ -95,6 +89,7 @@ public class SecurityTokenConnection {
private final Transport mTransport; private final Transport mTransport;
@NonNull @NonNull
private final Passphrase mPin; private final Passphrase mPin;
private final CommandAPDUFactory commandFactory;
private CardCapabilities mCardCapabilities; private CardCapabilities mCardCapabilities;
private OpenPgpCapabilities mOpenPgpCapabilities; private OpenPgpCapabilities mOpenPgpCapabilities;
@@ -115,6 +110,8 @@ public class SecurityTokenConnection {
private SecurityTokenConnection(@NonNull Transport transport, @NonNull Passphrase pin) { private SecurityTokenConnection(@NonNull Transport transport, @NonNull Passphrase pin) {
this.mTransport = transport; this.mTransport = transport;
this.mPin = pin; this.mPin = pin;
commandFactory = new CommandAPDUFactory();
} }
private String getHolderName(byte[] name) { private String getHolderName(byte[] name) {
@@ -186,7 +183,7 @@ public class SecurityTokenConnection {
// Connect on smartcard layer // Connect on smartcard layer
// Command APDU (page 51) for SELECT FILE command (page 29) // Command APDU (page 51) for SELECT FILE command (page 29)
CommandAPDU select = new CommandAPDU(0x00, 0xA4, 0x04, 0x00, Hex.decode("D27600012401")); CommandAPDU select = commandFactory.createSelectFileCommand("D27600012401");
ResponseAPDU response = communicate(select); // activate connection ResponseAPDU response = communicate(select); // activate connection
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
@@ -202,7 +199,7 @@ public class SecurityTokenConnection {
if (mOpenPgpCapabilities.isHasSCP11bSM()) { if (mOpenPgpCapabilities.isHasSCP11bSM()) {
try { try {
SCP11bSecureMessaging.establish(this, context); SCP11bSecureMessaging.establish(this, context, commandFactory);
} catch (SecureMessagingException e) { } catch (SecureMessagingException e) {
mSecureMessaging = null; mSecureMessaging = null;
Log.e(Constants.TAG, "failed to establish secure messaging", e); Log.e(Constants.TAG, "failed to establish secure messaging", e);
@@ -225,7 +222,7 @@ public class SecurityTokenConnection {
} }
// Command APDU for RESET RETRY COUNTER command (page 33) // Command APDU for RESET RETRY COUNTER command (page 33)
CommandAPDU changePin = new CommandAPDU(0x00, 0x2C, 0x02, 0x81, newPin); CommandAPDU changePin = commandFactory.createResetRetryCounter(newPin);
ResponseAPDU response = communicate(changePin); ResponseAPDU response = communicate(changePin);
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
@@ -269,7 +266,7 @@ public class SecurityTokenConnection {
} }
// Command APDU for CHANGE REFERENCE DATA command (page 32) // Command APDU for CHANGE REFERENCE DATA command (page 32)
CommandAPDU changePin = new CommandAPDU(0x00, 0x24, 0x00, pw, Arrays.concatenate(pin, newPin)); CommandAPDU changePin = commandFactory.createChangeReferenceDataCommand(pw, newPin, pin);
ResponseAPDU response = communicate(changePin); ResponseAPDU response = communicate(changePin);
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
@@ -349,7 +346,7 @@ public class SecurityTokenConnection {
throw new CardException("Unknown encryption key type!"); throw new CardException("Unknown encryption key type!");
} }
CommandAPDU command = new CommandAPDU(0x00, 0x2A, 0x80, 0x86, data, MAX_APDU_NE_EXT); CommandAPDU command = commandFactory.createDecipherCommand(data);
ResponseAPDU response = communicate(command); ResponseAPDU response = communicate(command);
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
@@ -466,7 +463,7 @@ public class SecurityTokenConnection {
verifyAdminPin(adminPin); verifyAdminPin(adminPin);
} }
CommandAPDU command = new CommandAPDU(0x00, 0xDA, (dataObject & 0xFF00) >> 8, dataObject & 0xFF, data); CommandAPDU command = commandFactory.createPutDataCommand(dataObject, data);
ResponseAPDU response = communicate(command); // put data ResponseAPDU response = communicate(command); // put data
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
@@ -474,7 +471,6 @@ public class SecurityTokenConnection {
} }
} }
private void setKeyAttributes(Passphrase adminPin, final KeyType slot, final CanonicalizedSecretKey secretKey) private void setKeyAttributes(Passphrase adminPin, final KeyType slot, final CanonicalizedSecretKey secretKey)
throws IOException { throws IOException {
@@ -566,7 +562,7 @@ public class SecurityTokenConnection {
throw new IOException(e.getMessage()); throw new IOException(e.getMessage());
} }
CommandAPDU apdu = new CommandAPDU(0x00, 0xDB, 0x3F, 0xFF, keyBytes); CommandAPDU apdu = commandFactory.createPutKeyCommand(keyBytes);
ResponseAPDU response = communicate(apdu); ResponseAPDU response = communicate(apdu);
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
@@ -581,7 +577,7 @@ public class SecurityTokenConnection {
* @return The fingerprints of all subkeys in a contiguous byte array. * @return The fingerprints of all subkeys in a contiguous byte array.
*/ */
public byte[] getFingerprints() throws IOException { public byte[] getFingerprints() throws IOException {
CommandAPDU apdu = new CommandAPDU(0x00, 0xCA, 0x00, 0x6E, MAX_APDU_NE_EXT); CommandAPDU apdu = commandFactory.createGetDataCommand(0x00, 0x6E);
ResponseAPDU response = communicate(apdu); ResponseAPDU response = communicate(apdu);
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
@@ -629,7 +625,7 @@ public class SecurityTokenConnection {
} }
private byte[] getData(int p1, int p2) throws IOException { private byte[] getData(int p1, int p2) throws IOException {
ResponseAPDU response = communicate(new CommandAPDU(0x00, 0xCA, p1, p2, MAX_APDU_NE_EXT)); ResponseAPDU response = communicate(commandFactory.createGetDataCommand(p1, p2));
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
throw new CardException("Failed to get pw status bytes", response.getSW()); throw new CardException("Failed to get pw status bytes", response.getSW());
} }
@@ -711,7 +707,7 @@ public class SecurityTokenConnection {
} }
// Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37) // Command APDU for PERFORM SECURITY OPERATION: COMPUTE DIGITAL SIGNATURE (page 37)
CommandAPDU command = new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, data, MAX_APDU_NE_EXT); CommandAPDU command = commandFactory.createComputeDigitalSignatureCommand(data);
ResponseAPDU response = communicate(command); ResponseAPDU response = communicate(command);
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
@@ -756,7 +752,6 @@ public class SecurityTokenConnection {
return signature; return signature;
} }
/** /**
* Transceives APDU * Transceives APDU
* Splits extended APDU into short APDUs and chains them if necessary * Splits extended APDU into short APDUs and chains them if necessary
@@ -776,45 +771,36 @@ public class SecurityTokenConnection {
} }
} }
ByteArrayOutputStream result = new ByteArrayOutputStream();
ResponseAPDU lastResponse = null; ResponseAPDU lastResponse = null;
// Transmit // Transmit
if (mCardCapabilities.hasExtended()) { if (mCardCapabilities.hasExtended()) {
lastResponse = mTransport.transceive(apdu); lastResponse = mTransport.transceive(apdu);
} else if (apdu.getData().length <= MAX_APDU_NC) { } else if (commandFactory.isSuitableForShortApdu(apdu)) {
int ne = Math.min(apdu.getNe(), MAX_APDU_NE); CommandAPDU shortApdu = commandFactory.createShortApdu(apdu);
lastResponse = mTransport.transceive(new CommandAPDU(apdu.getCLA(), apdu.getINS(), lastResponse = mTransport.transceive(shortApdu);
apdu.getP1(), apdu.getP2(), apdu.getData(), ne)); } else if (mCardCapabilities.hasChaining()) {
} else if (apdu.getData().length > MAX_APDU_NC && mCardCapabilities.hasChaining()) { List<CommandAPDU> chainedApdus = commandFactory.createChainedApdus(apdu);
int offset = 0; for (int i = 0, totalCommands = chainedApdus.size(); i < totalCommands; i++) {
byte[] data = apdu.getData(); CommandAPDU chainedApdu = chainedApdus.get(i);
int ne = Math.min(apdu.getNe(), MAX_APDU_NE); lastResponse = mTransport.transceive(chainedApdu);
while (offset < data.length) {
int curLen = Math.min(MAX_APDU_NC, data.length - offset);
boolean last = offset + curLen >= data.length;
int cla = apdu.getCLA() + (last ? 0 : MASK_CLA_CHAINING);
lastResponse = mTransport.transceive(new CommandAPDU(cla, apdu.getINS(), apdu.getP1(), boolean isLastCommand = i < totalCommands - 1;
apdu.getP2(), data, offset, curLen, ne)); if (isLastCommand && lastResponse.getSW() != APDU_SW_SUCCESS) {
if (!last && lastResponse.getSW() != APDU_SW_SUCCESS) {
throw new UsbTransportException("Failed to chain apdu (last SW: " + lastResponse.getSW() + ")"); throw new UsbTransportException("Failed to chain apdu (last SW: " + lastResponse.getSW() + ")");
} }
offset += curLen;
} }
} }
if (lastResponse == null) { if (lastResponse == null) {
throw new UsbTransportException("Can't transmit command"); throw new UsbTransportException("Can't transmit command");
} }
ByteArrayOutputStream result = new ByteArrayOutputStream();
result.write(lastResponse.getData()); result.write(lastResponse.getData());
// Receive // Receive
while (lastResponse.getSW1() == APDU_SW1_RESPONSE_AVAILABLE) { while (lastResponse.getSW1() == APDU_SW1_RESPONSE_AVAILABLE) {
// GET RESPONSE ISO/IEC 7816-4 par.7.6.1 // GET RESPONSE ISO/IEC 7816-4 par.7.6.1
CommandAPDU getResponse = new CommandAPDU(0x00, 0xC0, 0x00, 0x00, lastResponse.getSW2()); CommandAPDU getResponse = commandFactory.createGetResponseCommand(lastResponse.getSW2());
lastResponse = mTransport.transceive(getResponse); lastResponse = mTransport.transceive(getResponse);
result.write(lastResponse.getData()); result.write(lastResponse.getData());
} }
@@ -841,7 +827,7 @@ public class SecurityTokenConnection {
try { try {
// By trying to select any apps that have the Fidesmo AID prefix we can // By trying to select any apps that have the Fidesmo AID prefix we can
// see if it is a Fidesmo device or not // see if it is a Fidesmo device or not
CommandAPDU apdu = new CommandAPDU(0x00, 0xA4, 0x04, 0x00, Hex.decode(FIDESMO_APPS_AID_PREFIX)); CommandAPDU apdu = commandFactory.createSelectFileCommand(FIDESMO_APPS_AID_PREFIX);
return communicate(apdu).getSW() == APDU_SW_SUCCESS; return communicate(apdu).getSW() == APDU_SW_SUCCESS;
} catch (IOException e) { } catch (IOException e) {
Log.e(Constants.TAG, "Card communication failed!", e); Log.e(Constants.TAG, "Card communication failed!", e);
@@ -873,7 +859,7 @@ public class SecurityTokenConnection {
verifyAdminPin(adminPin); verifyAdminPin(adminPin);
} }
CommandAPDU apdu = new CommandAPDU(0x00, 0x47, 0x80, 0x00, new byte[]{(byte) slot, 0x00}, MAX_APDU_NE_EXT); CommandAPDU apdu = commandFactory.createGenerateKeyCommand(slot);
ResponseAPDU response = communicate(apdu); ResponseAPDU response = communicate(apdu);
if (response.getSW() != APDU_SW_SUCCESS) { if (response.getSW() != APDU_SW_SUCCESS) {
@@ -885,7 +871,7 @@ public class SecurityTokenConnection {
private ResponseAPDU tryPin(int mode, byte[] pin) throws IOException { private ResponseAPDU tryPin(int mode, byte[] pin) throws IOException {
// Command APDU for VERIFY command (page 32) // Command APDU for VERIFY command (page 32)
return communicate(new CommandAPDU(0x00, 0x20, 0x00, mode, pin)); return communicate(commandFactory.createVerifyCommand(mode, pin));
} }
/** /**
@@ -918,8 +904,8 @@ public class SecurityTokenConnection {
// reactivate token! // reactivate token!
// NOTE: keep the order here! First execute _both_ reactivate commands. Before checking _both_ responses // NOTE: keep the order here! First execute _both_ reactivate commands. Before checking _both_ responses
// If a token is in a bad state and reactivate1 fails, it could still be reactivated with reactivate2 // If a token is in a bad state and reactivate1 fails, it could still be reactivated with reactivate2
CommandAPDU reactivate1 = new CommandAPDU(0x00, 0xE6, 0x00, 0x00); CommandAPDU reactivate1 = commandFactory.createReactivate1Command();
CommandAPDU reactivate2 = new CommandAPDU(0x00, 0x44, 0x00, 0x00); CommandAPDU reactivate2 = commandFactory.createReactivate2Command();
ResponseAPDU response1 = communicate(reactivate1); ResponseAPDU response1 = communicate(reactivate1);
ResponseAPDU response2 = communicate(reactivate2); ResponseAPDU response2 = communicate(reactivate2);
if (response1.getSW() != APDU_SW_SUCCESS) { if (response1.getSW() != APDU_SW_SUCCESS) {