Refactor the method for authentication certificate retrieval.

This commit is contained in:
Lemmo Lavonen 2021-10-13 02:27:19 +03:00
parent 9c48cc9c1a
commit 29c7ecfa12

View File

@ -12,7 +12,6 @@ import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.encoders.Hex;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -22,10 +21,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
@ -101,13 +97,8 @@ public class Comms {
public Comms(IsoDep idCard, String CAN) throws IOException, NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { public Comms(IsoDep idCard, String CAN) throws IOException, NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
idCard.connect(); idCard.connect();
this.idCard = idCard; this.idCard = idCard;
long start = System.currentTimeMillis();
byte[][] keys = PACE(CAN); byte[][] keys = PACE(CAN);
Log.i("Pace duration", String.valueOf(System.currentTimeMillis() - start));
keyEnc = keys[0]; keyEnc = keys[0];
keyMAC = keys[1]; keyMAC = keys[1];
} }
@ -228,7 +219,7 @@ public class Comms {
APDU = createAPDU(dataForMACIncomplete, publicKey.getEncoded(false), 65); APDU = createAPDU(dataForMACIncomplete, publicKey.getEncoded(false), 65);
MAC = getMAC(APDU, keyMAC); MAC = getMAC(APDU, keyMAC);
if (!Hex.toHexString(response, 4, 8).equals(Hex.toHexString(MAC))) { if (!Hex.toHexString(response, 4, 8).equals(Hex.toHexString(MAC))) {
throw new RuntimeException("Could not verify chip's MAC."); // Should never happen. throw new RuntimeException("Could not verify chip's MAC."); // *Should* never happen.
} }
return new byte[][]{keyEnc, keyMAC}; return new byte[][]{keyEnc, keyMAC};
@ -302,7 +293,6 @@ public class Comms {
} }
System.arraycopy(new byte[]{-114, 8}, 0, APDU, incomplete.length + encryptedData.length, 2); // MAC is encapsulated using the tag 0x8E System.arraycopy(new byte[]{-114, 8}, 0, APDU, incomplete.length + encryptedData.length, 2); // MAC is encapsulated using the tag 0x8E
System.arraycopy(MAC, 0, APDU, incomplete.length + encryptedData.length + 2, MAC.length); System.arraycopy(MAC, 0, APDU, incomplete.length + encryptedData.length + 2, MAC.length);
ssc++; ssc++;
return APDU; return APDU;
@ -325,7 +315,6 @@ public class Comms {
* *
* @param lastBytes the last bytes of the personal data file identifiers (0 < x < 16) * @param lastBytes the last bytes of the personal data file identifiers (0 < x < 16)
* @return array containing the corresponding data strings * @return array containing the corresponding data strings
*
*/ */
public String[] readPersonalData(byte[] lastBytes) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException { public String[] readPersonalData(byte[] lastBytes) throws NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, IOException {
@ -353,7 +342,6 @@ public class Comms {
personalData[stringIndex++] = new String(Arrays.copyOfRange(response, 0, indexOfTerminator)); personalData[stringIndex++] = new String(Arrays.copyOfRange(response, 0, indexOfTerminator));
} }
return personalData; return personalData;
} }
@ -364,16 +352,23 @@ public class Comms {
* @param PIN1 byte array containing the PIN1 * @param PIN1 byte array containing the PIN1
*/ */
private void verifyPIN1(byte[] PIN1) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException { private void verifyPIN1(byte[] PIN1) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException, IOException {
selectIASECCApplication(); selectIASECCApplication();
byte[] paddedPIN1 = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; byte[] paddedPIN1 = new byte[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
System.arraycopy(PIN1, 0, paddedPIN1, 0, PIN1.length); System.arraycopy(PIN1, 0, paddedPIN1, 0, PIN1.length);
byte[] APDU = createSecureAPDU(paddedPIN1, verifyPIN1); byte[] APDU = createSecureAPDU(paddedPIN1, verifyPIN1);
byte[] response = idCard.transceive(APDU); byte[] response = idCard.transceive(APDU);
Log.i("PIN1 verification", Hex.toHexString(response)); Log.i("PIN1 verification", Hex.toHexString(response));
String sw1sw2 = Hex.toHexString(response, response.length - 2, 2);
if (!sw1sw2.equals("9000")) { byte sw1 = response[response.length - 2];
if (sw1sw2.equals("6983")) throw new RuntimeException("Invalid PIN1. Authentication method blocked."); byte sw2 = response[response.length - 1];
else throw new RuntimeException(String.format("Invalid PIN1. %c attempt%s left.", sw1sw2.charAt(sw1sw2.length() - 1), sw1sw2.endsWith("1") ? "" : "s")); if (sw1 != -112 || sw2 != 0) {
if (sw1 == 105 && sw2 == -125) {
throw new RuntimeException("Invalid PIN1. Authentication method blocked.");
} else {
throw new RuntimeException(String.format("Invalid PIN1. %d attempt%s left.", sw2 + 64, sw2 == -63 ? "" : "s"));
}
} }
} }
@ -388,48 +383,49 @@ public class Comms {
byte[] APDU = createSecureAPDU(new byte[]{-83, -15}, selectFile); byte[] APDU = createSecureAPDU(new byte[]{-83, -15}, selectFile);
byte[] response = idCard.transceive(APDU); byte[] response = idCard.transceive(APDU);
Log.i("Select AWP Application", Hex.toHexString(response)); Log.i("Select AWP application", Hex.toHexString(response));
APDU = createSecureAPDU(new byte[]{52, 1}, selectFile); APDU = createSecureAPDU(new byte[]{52, 1}, selectFile);
response = idCard.transceive(APDU); response = idCard.transceive(APDU);
Log.i("Select certificate", Hex.toHexString(response)); Log.i("Select certificate", Hex.toHexString(response));
byte[] responses = new byte[0]; byte[] certificate = new byte[0];
byte[] readCert = Arrays.copyOf(read, read.length); byte[] readCert = Arrays.copyOf(read, read.length);
int indexOfTerminator = 0; // Construct the certificate byte array n=indexOfTerminator bytes at a time
for (int i = 0; i < 9; i++) { for (int i = 0; i < 16; i++) {
readCert[2] = (byte) ((byte) i / 2); // Set the P1/P2 values to incrementally read the certificate
readCert[3] = (byte) ((byte) (i % 2) * 25); readCert[2] = (byte) (certificate.length / 256);
readCert[3] = (byte) (certificate.length % 256);
APDU = createSecureAPDU(new byte[0], readCert); APDU = createSecureAPDU(new byte[0], readCert);
response = idCard.transceive(APDU); response = idCard.transceive(APDU);
Log.i("Read certificate part " + i, Hex.toHexString(response)); Log.i("Read certificate", Hex.toHexString(response));
if (!Hex.toHexString(response).substring(response.length * 2 - 4).equals("6b00")) { byte sw1 = response[response.length - 2];
byte[] decrypted = encryptDecryptData(Arrays.copyOfRange(response, 4, 244), Cipher.DECRYPT_MODE); byte sw2 = response[response.length - 1];
if (i % 2 == 0) { if (sw1 == 107 && sw2 == 0) {
indexOfTerminator = Hex.toHexString(decrypted).lastIndexOf("80") / 2; throw new RuntimeException("Wrong read parameters.");
responses = Arrays.copyOf(responses, responses.length + indexOfTerminator);
System.arraycopy(decrypted, 0, responses, responses.length - indexOfTerminator, indexOfTerminator);
// Log.i("Partial certificate #1", new String(Arrays.copyOf(decrypted, indexOfTerminator), StandardCharsets.ISO_8859_1));
} else {
int newIndexOfTerminator = Hex.toHexString(decrypted).lastIndexOf("80") / 2;
responses = Arrays.copyOf(responses, responses.length + 25 - indexOfTerminator + newIndexOfTerminator);
System.arraycopy(decrypted, 0, responses, responses.length - newIndexOfTerminator, newIndexOfTerminator);
// Log.i("Partial certificate #2", new String(Arrays.copyOfRange(decrypted, newIndexOfTerminator - 25, newIndexOfTerminator), StandardCharsets.ISO_8859_1));
} }
} else {
// Set the range containing a portion of the certificate and decrypt it
int start = response[2] == 1 ? 3 : 4;
int end = start + (response[start - 2] + 256) % 256 - 1;
byte[] decrypted = encryptDecryptData(Arrays.copyOfRange(response, start, end), Cipher.DECRYPT_MODE);
int indexOfTerminator = Hex.toHexString(decrypted).lastIndexOf("80") / 2;
certificate = Arrays.copyOf(certificate, certificate.length + indexOfTerminator);
System.arraycopy(decrypted, 0, certificate, certificate.length - indexOfTerminator, indexOfTerminator);
if (sw1 == -112 && sw2 == 0) {
break; break;
} }
} }
CertificateFactory certificateFactory = CertificateFactory.getInstance("X509"); // For debugging, ascertain that the byte array corresponds to a valid certificate
X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(responses)); // CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
// X509Certificate x509Certificate = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certificate));
// Log.i("Certificate subject", x509Certificate.getSubjectX500Principal().getName());
Log.i("Certificate subject", certificate.getSubjectX500Principal().getName()); return certificate;
return responses;
} }
} }