From 82cf3f8e1e3f69826fdd6ee6f8cb53c1cf5bdc91 Mon Sep 17 00:00:00 2001 From: Martin Paljak Date: Mon, 30 Sep 2024 08:48:43 +0300 Subject: [PATCH] Properly parse (also the invalid) SCP version from card recognition data --- .../src/main/java/pro/javacard/gp/GPData.java | 7 +++-- .../javacard/gp/GPSecureChannelVersion.java | 2 +- .../main/java/pro/javacard/gp/GPSession.java | 7 +++-- .../main/java/pro/javacard/gptool/Key.java | 26 +++++++++++++++---- .../pro/javacard/gptool/TestKeyConverter.java | 2 +- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/library/src/main/java/pro/javacard/gp/GPData.java b/library/src/main/java/pro/javacard/gp/GPData.java index ca4fade1..8ad7592a 100644 --- a/library/src/main/java/pro/javacard/gp/GPData.java +++ b/library/src/main/java/pro/javacard/gp/GPData.java @@ -124,11 +124,10 @@ public static void pretty_print_card_data(byte[] data) { for (BerTlv ot : vt.getValues()) { byte[] oidBytes = ot.getBytesValue(); String oid = logAndGetOidFromByteArray(ot.getTag().bytes, oidBytes); - if (oid.startsWith("1.2.840.114283.4")) { + // This also works with the invalid encoding for SCP80 i=00 + if (oid.startsWith("1.2.840.114283.4.")) { byte[] scp = Arrays.copyOfRange(oidBytes, oidBytes.length - 2, oidBytes.length); - if (scp.length == 2) { - System.out.printf("-> GP SCP%02x i=%02x%n", scp[0], scp[1]); - } + System.out.printf("-> GP %s%n", GPSecureChannelVersion.valueOf(scp[0] & 0xFF, scp[1] & 0xFF)); } } } else if (vt.isTag(new BerTag(0x65))) { diff --git a/library/src/main/java/pro/javacard/gp/GPSecureChannelVersion.java b/library/src/main/java/pro/javacard/gp/GPSecureChannelVersion.java index 4dc8e1b2..d545b3b8 100644 --- a/library/src/main/java/pro/javacard/gp/GPSecureChannelVersion.java +++ b/library/src/main/java/pro/javacard/gp/GPSecureChannelVersion.java @@ -26,7 +26,7 @@ public final class GPSecureChannelVersion { public final SCP scp; public enum SCP { - SCP01(1), SCP02(2), SCP03(3); + SCP01(1), SCP02(2), SCP03(3), SCP80(0x80); private final int value; diff --git a/library/src/main/java/pro/javacard/gp/GPSession.java b/library/src/main/java/pro/javacard/gp/GPSession.java index 0e094323..059eeb85 100644 --- a/library/src/main/java/pro/javacard/gp/GPSession.java +++ b/library/src/main/java/pro/javacard/gp/GPSession.java @@ -304,9 +304,12 @@ private void parse_select_response(byte[] fci) throws GPException { logger.debug("Auto-detected GP version: " + GPData.oid2version(veroid.getBytesValue())); } } + } else if (GPData.oid2string(oidtag.getBytesValue()).startsWith("1.2.840.114283.4.") && oidtag.getBytesValue().length == 9) { + byte[] data = oidtag.getBytesValue(); + // SCP version + logger.debug("Auto-detected SCP version: {}", GPSecureChannelVersion.valueOf(data[7] & 0xFF, data[8] & 0xFF)); } else { - // FIXME: this is BS here. - logger.warn("Invalid CardRecognitionData", oidtag.getBytesValue()); + logger.warn("Unrecognized card recgnition data: {}", HexUtils.bin2hex(oidtag.getBytesValue())); } } else { logger.warn("Not global platform OID"); diff --git a/tool/src/main/java/pro/javacard/gptool/Key.java b/tool/src/main/java/pro/javacard/gptool/Key.java index 2e932e06..c33f763e 100644 --- a/tool/src/main/java/pro/javacard/gptool/Key.java +++ b/tool/src/main/java/pro/javacard/gptool/Key.java @@ -21,6 +21,7 @@ import apdu4j.core.HexUtils; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.pkcs.RSAPrivateKey; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; @@ -32,14 +33,15 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; +import java.security.*; import java.security.cert.CertificateException; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.spec.RSAPublicKeySpec; import java.util.Optional; // Helper to convert command line parameters to meaningful key objects. @@ -90,10 +92,24 @@ public static Key valueOf(String v) { throw new IllegalArgumentException("Can not read certificate from PEM: " + ce.getMessage()); } } else if (ohh instanceof PrivateKeyInfo) { - return new Key(v, null, null, new JcaPEMKeyConverter().getPrivateKey((PrivateKeyInfo) ohh)); + PrivateKey pk = new JcaPEMKeyConverter().getPrivateKey((PrivateKeyInfo) ohh); + // TODO: do this for other key types as well + if (pk instanceof RSAPrivateKey) { + BigInteger modulus = ((RSAPrivateKey) pk).getModulus(); + BigInteger exponent = ((RSAPrivateKey) pk).getPublicExponent(); + PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent)); + return new Key(v, null, publicKey, pk); + } else if (pk instanceof RSAPrivateCrtKey) { + BigInteger modulus = ((RSAPrivateCrtKey) pk).getModulus(); + BigInteger exponent = ((RSAPrivateCrtKey) pk).getPublicExponent(); + PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(modulus, exponent)); + return new Key(v, null, publicKey, pk); + } else { + return new Key(v, null, null, pk); + } } else throw new IllegalArgumentException("Can not read PEM"); } - } catch (IOException e) { + } catch (IOException | GeneralSecurityException e) { throw new IllegalArgumentException("Could not read PEM: " + e.getMessage(), e); } } else { diff --git a/tool/src/test/java/pro/javacard/gptool/TestKeyConverter.java b/tool/src/test/java/pro/javacard/gptool/TestKeyConverter.java index a8d9f0b6..70f6b0a6 100644 --- a/tool/src/test/java/pro/javacard/gptool/TestKeyConverter.java +++ b/tool/src/test/java/pro/javacard/gptool/TestKeyConverter.java @@ -40,7 +40,7 @@ public void testBadFile() { public void testPrivateOnly() { Key k = Key.valueOf("../library/src/test/resources/test-dap-rsa-1k-priv.pem"); Assert.assertFalse(k.getSymmetric().isPresent()); - Assert.assertFalse(k.getPublic().isPresent()); + Assert.assertTrue(k.getPublic().isPresent()); Assert.assertTrue(k.getPrivate().isPresent()); }