mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2024-12-22 12:30:16 +02:00
Add a method for verifying PIN1.
This commit is contained in:
parent
86c6a32032
commit
d2ad8920a1
@ -21,6 +21,7 @@ import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
@ -30,7 +31,7 @@ import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class Comms {
|
||||
private static final byte[] master = { // select Main AID
|
||||
private static final byte[] selectMain = { // select IAS-ECC
|
||||
0, -92, 4, 12, 16, -96, 0, 0, 0, 119, 1, 8, 0, 7, 0, 0, -2, 0, 0, 1, 0
|
||||
};
|
||||
|
||||
@ -58,19 +59,31 @@ public class Comms {
|
||||
127, 73, 79, 6, 10, 4, 0, 127, 0, 7, 2, 2, 4, 2, 4, -122, 65
|
||||
};
|
||||
|
||||
private static final byte[] masterSec = {
|
||||
private static final byte[] selectAppSecure = {
|
||||
12, -92, 4, 12, 45, -121, 33, 1
|
||||
};
|
||||
|
||||
private static final byte[] personal = { // select personal data DF
|
||||
private static final byte[] IASECCAID = { // Identification Authentication Signature - European Citizen Card Application Identifier
|
||||
-96, 0, 0, 0, 119, 1, 8, 0, 7, 0, 0, -2, 0, 0, 1, 0
|
||||
};
|
||||
|
||||
private static final byte[] QSCDAID = { // Qualified Signature Creation Device Application Identifier
|
||||
81, 83, 67, 68, 32, 65, 112, 112, 108, 105, 99, 97, 116, 105, 111, 110
|
||||
};
|
||||
|
||||
private static final byte[] selectFile = { //
|
||||
12, -92, 1, 12, 29, -121, 17, 1
|
||||
};
|
||||
|
||||
private static final byte[] read = { // read binary
|
||||
private static final byte[] read = {
|
||||
12, -80, 0, 0, 13, -105, 1, 0
|
||||
};
|
||||
|
||||
private IsoDep idCard;
|
||||
private static final byte[] verifyPIN1 = {
|
||||
12, 32, 0, 1, 29, -121, 17, 1
|
||||
};
|
||||
|
||||
private final IsoDep idCard;
|
||||
private final byte[] keyEnc;
|
||||
private final byte[] keyMAC;
|
||||
private byte ssc; // Send sequence counter.
|
||||
@ -95,10 +108,6 @@ public class Comms {
|
||||
keyMAC = keys[1];
|
||||
}
|
||||
|
||||
public byte[] getAuthenticationCertificate() {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the message authentication code
|
||||
*
|
||||
@ -165,8 +174,8 @@ public class Comms {
|
||||
*/
|
||||
private byte[][] PACE(String CAN) throws IOException, NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException {
|
||||
|
||||
// select the ECC applet on the chip
|
||||
byte[] response = idCard.transceive(master);
|
||||
// select the IAS-ECC application on the chip
|
||||
byte[] response = idCard.transceive(selectMain);
|
||||
Log.i("Select applet", Hex.toHexString(response));
|
||||
|
||||
// initiate PACE
|
||||
@ -221,6 +230,22 @@ public class Comms {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a file and reads its contents
|
||||
*
|
||||
* @param FID file identifier of the required file
|
||||
* @return decrypted file contents
|
||||
*/
|
||||
private byte[] readFileByFID(byte[] FID) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
|
||||
byte [] APDU = createSecureAPDU(FID, selectFile);
|
||||
byte [] response = idCard.transceive(APDU);
|
||||
Log.i(String.format("Select FID %s", Hex.toHexString(FID)), Hex.toHexString(response));
|
||||
APDU = createSecureAPDU(new byte[0], read);
|
||||
response = idCard.transceive(APDU);
|
||||
Log.i("Read binary", Hex.toHexString(response));
|
||||
return encryptDecryptData(Arrays.copyOfRange(response, 3, 19), Cipher.DECRYPT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts or decrypts the APDU data
|
||||
*
|
||||
@ -267,8 +292,7 @@ public class Comms {
|
||||
byte[] MAC = getMAC(macData, keyMAC);
|
||||
|
||||
// construct the APDU using the encrypted data and the MAC
|
||||
byte[] APDU = new byte[incomplete.length + encryptedData.length + MAC.length + 3];
|
||||
System.arraycopy(incomplete, 0, APDU, 0, incomplete.length);
|
||||
byte[] APDU = Arrays.copyOf(incomplete, incomplete.length + encryptedData.length + MAC.length + 3);
|
||||
if (encryptedData.length > 0) {
|
||||
System.arraycopy(encryptedData, 0, APDU, incomplete.length, encryptedData.length);
|
||||
}
|
||||
@ -280,49 +304,79 @@ public class Comms {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the IAS ECC application, which provides the PKI functionalities, after a successful PACE.
|
||||
*/
|
||||
private void selectIASECCApplication() throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
|
||||
byte[] APDU = createSecureAPDU(IASECCAID, selectAppSecure);
|
||||
byte[] response = idCard.transceive(APDU);
|
||||
Log.i("Select the main application", Hex.toHexString(response));
|
||||
if (!Hex.toHexString(response, response.length - 2, 2).equals("9000")) {
|
||||
throw new RuntimeException("Could not select IAS-ECC."); // *Should* never happen.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the contents of the personal data dedicated file
|
||||
*
|
||||
* @param FID the last bytes of file identifiers being requested
|
||||
* @return array containing the data strings
|
||||
* @param lastBytes the last bytes of the personal data file identifiers (0 < x < 16)
|
||||
* @return array containing the corresponding data strings
|
||||
*
|
||||
*/
|
||||
public String[] readPersonalData(byte[] FID) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
|
||||
public String[] readPersonalData(byte[] lastBytes) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
|
||||
|
||||
String[] personalData = new String[FID.length];
|
||||
byte[] data;
|
||||
byte[] APDU;
|
||||
byte[] response;
|
||||
String[] personalData = new String[lastBytes.length];
|
||||
int stringIndex = 0;
|
||||
|
||||
// select the application
|
||||
selectIASECCApplication();
|
||||
|
||||
// select the personal data dedicated file
|
||||
data = new byte[]{80, 0}; // personal data DF FID
|
||||
APDU = createSecureAPDU(data, personal);
|
||||
response = idCard.transceive(APDU);
|
||||
byte[] FID = new byte[]{80, 0}; // personal data dedicated file FID
|
||||
byte[] APDU = createSecureAPDU(FID, selectFile);
|
||||
byte[] response = idCard.transceive(APDU);
|
||||
Log.i("Select personal data DF", Hex.toHexString(response));
|
||||
|
||||
// select and read the first 8 elementary files in the DF
|
||||
for (int i = 0; i < FID.length; i++) {
|
||||
// select and read the personal data elementary files
|
||||
for (byte index : lastBytes) {
|
||||
|
||||
byte index = FID[i];
|
||||
if (index > 15 || index < 1) throw new RuntimeException("Invalid personal data FID.");
|
||||
|
||||
data[1] = index;
|
||||
APDU = createSecureAPDU(data, personal);
|
||||
response = idCard.transceive(APDU);
|
||||
Log.i(String.format("Select EF 500%d", index), Hex.toHexString(response));
|
||||
|
||||
APDU = createSecureAPDU(new byte[0], read);
|
||||
response = idCard.transceive(APDU);
|
||||
Log.i(String.format("Read binary EF 500%d", index), Hex.toHexString(response));
|
||||
FID[1] = index;
|
||||
|
||||
// store the decrypted datum
|
||||
byte[] raw = encryptDecryptData(Arrays.copyOfRange(response, 3, 19), Cipher.DECRYPT_MODE);
|
||||
int indexOfTerminator = Hex.toHexString(raw).lastIndexOf("80") / 2;
|
||||
personalData[i] = new String(Arrays.copyOfRange(raw, 0, indexOfTerminator));
|
||||
response = readFileByFID(FID);
|
||||
int indexOfTerminator = Hex.toHexString(response).lastIndexOf("80") / 2;
|
||||
personalData[stringIndex++] = new String(Arrays.copyOfRange(response, 0, indexOfTerminator));
|
||||
|
||||
}
|
||||
|
||||
return personalData;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to verify the user-provided PIN1
|
||||
*
|
||||
* @param PIN1 byte array containing the PIN1
|
||||
*/
|
||||
private void verifyPIN1(byte[] PIN1) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException {
|
||||
selectIASECCApplication();
|
||||
byte[] paddedPIN1 = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
|
||||
System.arraycopy(PIN1, 0, paddedPIN1, 0, PIN1.length);
|
||||
byte[] APDU = createSecureAPDU(paddedPIN1, verifyPIN1);
|
||||
byte[] response = idCard.transceive(APDU);
|
||||
Log.i("PIN1 verification", Hex.toHexString(response));
|
||||
String sw1sw2 = Hex.toHexString(response, response.length - 2, 2);
|
||||
if (!sw1sw2.equals("9000")) {
|
||||
if (sw1sw2.equals("6983")) throw new RuntimeException("Invalid PIN1. Authentication method blocked.");
|
||||
else throw new RuntimeException(String.format("Invalid PIN1. %c attempt%s left.", sw1sw2.charAt(sw1sw2.length() - 1), sw1sw2.endsWith("1") ? "" : "s"));
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getAuthenticationCertificate(String PIN1) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException {
|
||||
|
||||
return new byte[0];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user