From 94d0dfc746361a8d2218d4da49e4c224bb89d096 Mon Sep 17 00:00:00 2001 From: tzefferer Date: Fri, 29 Oct 2010 11:55:40 +0000 Subject: git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@811 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../at/gv/egiz/smcc/AbstractSignatureCard.java | 8 +- .../at/gv/egiz/smcc/AbstractT0SignatureCard.java | 16 - smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java | 2 +- smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java | 2 +- smcc/src/main/java/at/gv/egiz/smcc/PtEidCard.java | 2 +- .../resources/at/gv/egiz/smcc/DNIeCard.properties | 3 + smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java | 2622 ++++++++++++++++++++ 7 files changed, 2635 insertions(+), 20 deletions(-) delete mode 100644 smcc/src/main/java/at/gv/egiz/smcc/AbstractT0SignatureCard.java create mode 100644 smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties create mode 100644 smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java index db8bebf1..a31e746f 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -69,7 +69,13 @@ public abstract class AbstractSignatureCard implements SignatureCard { protected CardChannel getCardChannel() { - return new LogCardChannel(card_.getBasicChannel()); + if(card_.getProtocol().equalsIgnoreCase("T=0")) { + + return new T0CardChannel(card_.getBasicChannel()); + } else { + + return new LogCardChannel(card_.getBasicChannel()); + } } @Override diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractT0SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractT0SignatureCard.java deleted file mode 100644 index a837b3cd..00000000 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractT0SignatureCard.java +++ /dev/null @@ -1,16 +0,0 @@ -package at.gv.egiz.smcc; - -import javax.smartcardio.CardChannel; - -public abstract class AbstractT0SignatureCard extends AbstractSignatureCard - implements SignatureCard { - - - protected CardChannel getCardChannel() { - - return new T0CardChannel(getCard().getBasicChannel()); - } - - - -} diff --git a/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java b/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java index e5e9b57a..76569305 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/BELPICCard.java @@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory; import at.gv.egiz.smcc.util.ISO7816Utils; import at.gv.egiz.smcc.util.SMCCHelper; -public class BELPICCard extends AbstractT0SignatureCard implements SignatureCard { +public class BELPICCard extends AbstractSignatureCard implements SignatureCard { /** * Logging facility. diff --git a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java b/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java index e0cb2655..768ac959 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/DNIeCard.java @@ -15,7 +15,7 @@ import org.slf4j.LoggerFactory; import at.gv.egiz.smcc.pin.gui.PINGUI; -public class DNIeCard extends AbstractT0SignatureCard implements SignatureCard { +public class DNIeCard extends AbstractSignatureCard implements SignatureCard { private final Logger log = LoggerFactory.getLogger(DNIeCard.class); diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PtEidCard.java b/smcc/src/main/java/at/gv/egiz/smcc/PtEidCard.java index 27cea3be..61a27a6c 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PtEidCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PtEidCard.java @@ -47,7 +47,7 @@ import at.gv.egiz.smcc.pin.gui.PINGUI; import at.gv.egiz.smcc.util.ISO7816Utils; import at.gv.egiz.smcc.util.SMCCHelper; -public class PtEidCard extends AbstractT0SignatureCard { +public class PtEidCard extends AbstractSignatureCard { private final Logger log = LoggerFactory.getLogger(PtEidCard.class); diff --git a/smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties b/smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties new file mode 100644 index 00000000..fc6157bf --- /dev/null +++ b/smcc/src/main/resources/at/gv/egiz/smcc/DNIeCard.properties @@ -0,0 +1,3 @@ +#pin.name=PIN +sig.pin.name=PIN +sig.pin.length=8-16 \ No newline at end of file diff --git a/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java b/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java new file mode 100644 index 00000000..b4f5e640 --- /dev/null +++ b/smcc/src/test/java/at/gv/egiz/smcc/ESCardTest.java @@ -0,0 +1,2622 @@ +package at.gv.egiz.smcc; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.smartcardio.*; + +import at.gv.egiz.smcc.pin.gui.PINGUI; + +//import iaik.asn1.ASN1; +//import iaik.asn1.ASN1Object; +//import iaik.asn1.CodingException; +//import iaik.security.provider.IAIK; +//import iaik.security.rsa.RSAPrivateKey; +//import iaik.security.rsa.RSAPublicKey; +//import iaik.x509.X509Certificate; + +public class ESCardTest extends AbstractSignatureCard { + + private static final String ROOT_CA_MODULO = "EADEDA455332945039DAA404C8EBC4D3B7F5DC869283CDEA2F101E2AB54FB0D0B03D8F030DAF2458028288F54CE552F8FA57AB2FB103B112427E11131D1D27E10A5B500EAAE5D940301E30EB26C3E9066B257156ED639D70CCC090B863AFBB3BFED8C17BE7673034B9823E977ED657252927F9575B9FFF6691DB64F80B5E92CD"; + private static final String ROOT_CA_PUBEXP = "010001"; + + private static final String TERMINAL_MODULO = "DB2CB41E112BACFA2BD7C3D3D7967E84FB9434FC261F9D090A8983947DAF8488D3DF8FBDCC1F92493585E134A1B42DE519F463244D7ED384E26D516CC7A4FF7895B1992140043AACADFC12E856B202346AF8226B1A882137DC3C5A57F0D2815C1FCD4BB46FA9157FDFFD79EC3A10A824CCC1EB3CE0B6B4396AE236590016BA69"; + private static final String TERMINAL_PRIVEXP = "18B44A3D155C61EBF4E3261C8BB157E36F63FE30E9AF28892B59E2ADEB18CC8C8BAD284B9165819CA4DEC94AA06B69BCE81706D1C1B668EB128695E5F7FEDE18A908A3011A646A481D3EA71D8A387D474609BD57A882B182E047DE80E04B4221416BD39DFA1FAC0300641962ADB109E28CAF50061B68C9CABD9B00313C0F46ED"; + private static final String TERMINAL_PUBEXP = "010001"; + + private static final byte[] C_CV_CA = new byte[] { + + (byte) 0x7F, (byte) 0x21, (byte) 0x81, (byte) 0xCE, (byte) 0x5F, + (byte) 0x37, (byte) 0x81, (byte) 0x80, (byte) 0x3C, (byte) 0xBA, + (byte) 0xDC, (byte) 0x36, (byte) 0x84, (byte) 0xBE, (byte) 0xF3, + (byte) 0x20, (byte) 0x41, (byte) 0xAD, (byte) 0x15, (byte) 0x50, + (byte) 0x89, (byte) 0x25, (byte) 0x8D, (byte) 0xFD, (byte) 0x20, + (byte) 0xC6, (byte) 0x91, (byte) 0x15, (byte) 0xD7, (byte) 0x2F, + (byte) 0x9C, (byte) 0x38, (byte) 0xAA, (byte) 0x99, (byte) 0xAD, + (byte) 0x6C, (byte) 0x1A, (byte) 0xED, (byte) 0xFA, (byte) 0xB2, + (byte) 0xBF, (byte) 0xAC, (byte) 0x90, (byte) 0x92, (byte) 0xFC, + (byte) 0x70, (byte) 0xCC, (byte) 0xC0, (byte) 0x0C, (byte) 0xAF, + (byte) 0x48, (byte) 0x2A, (byte) 0x4B, (byte) 0xE3, (byte) 0x1A, + (byte) 0xFD, (byte) 0xBD, (byte) 0x3C, (byte) 0xBC, (byte) 0x8C, + (byte) 0x83, (byte) 0x82, (byte) 0xCF, (byte) 0x06, (byte) 0xBC, + (byte) 0x07, (byte) 0x19, (byte) 0xBA, (byte) 0xAB, (byte) 0xB5, + (byte) 0x6B, (byte) 0x6E, (byte) 0xC8, (byte) 0x07, (byte) 0x60, + (byte) 0xA4, (byte) 0xA9, (byte) 0x3F, (byte) 0xA2, (byte) 0xD7, + (byte) 0xC3, (byte) 0x47, (byte) 0xF3, (byte) 0x44, (byte) 0x27, + (byte) 0xF9, (byte) 0xFF, (byte) 0x5C, (byte) 0x8D, (byte) 0xE6, + (byte) 0xD6, (byte) 0x5D, (byte) 0xAC, (byte) 0x95, (byte) 0xF2, + (byte) 0xF1, (byte) 0x9D, (byte) 0xAC, (byte) 0x00, (byte) 0x53, + (byte) 0xDF, (byte) 0x11, (byte) 0xA5, (byte) 0x07, (byte) 0xFB, + (byte) 0x62, (byte) 0x5E, (byte) 0xEB, (byte) 0x8D, (byte) 0xA4, + (byte) 0xC0, (byte) 0x29, (byte) 0x9E, (byte) 0x4A, (byte) 0x21, + (byte) 0x12, (byte) 0xAB, (byte) 0x70, (byte) 0x47, (byte) 0x58, + (byte) 0x8B, (byte) 0x8D, (byte) 0x6D, (byte) 0xA7, (byte) 0x59, + (byte) 0x22, (byte) 0x14, (byte) 0xF2, (byte) 0xDB, (byte) 0xA1, + (byte) 0x40, (byte) 0xC7, (byte) 0xD1, (byte) 0x22, (byte) 0x57, + (byte) 0x9B, (byte) 0x5F, (byte) 0x38, (byte) 0x3D, (byte) 0x22, + (byte) 0x53, (byte) 0xC8, (byte) 0xB9, (byte) 0xCB, (byte) 0x5B, + (byte) 0xC3, (byte) 0x54, (byte) 0x3A, (byte) 0x55, (byte) 0x66, + (byte) 0x0B, (byte) 0xDA, (byte) 0x80, (byte) 0x94, (byte) 0x6A, + (byte) 0xFB, (byte) 0x05, (byte) 0x25, (byte) 0xE8, (byte) 0xE5, + (byte) 0x58, (byte) 0x6B, (byte) 0x4E, (byte) 0x63, (byte) 0xE8, + (byte) 0x92, (byte) 0x41, (byte) 0x49, (byte) 0x78, (byte) 0x36, + (byte) 0xD8, (byte) 0xD3, (byte) 0xAB, (byte) 0x08, (byte) 0x8C, + (byte) 0xD4, (byte) 0x4C, (byte) 0x21, (byte) 0x4D, (byte) 0x6A, + (byte) 0xC8, (byte) 0x56, (byte) 0xE2, (byte) 0xA0, (byte) 0x07, + (byte) 0xF4, (byte) 0x4F, (byte) 0x83, (byte) 0x74, (byte) 0x33, + (byte) 0x37, (byte) 0x37, (byte) 0x1A, (byte) 0xDD, (byte) 0x8E, + (byte) 0x03, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01, + (byte) 0x42, (byte) 0x08, (byte) 0x65, (byte) 0x73, (byte) 0x52, + (byte) 0x44, (byte) 0x49, (byte) 0x60, (byte) 0x00, (byte) 0x06 }; + + private static final byte[] CHR = new byte[] { + + (byte) 0x83, (byte) 0x08, (byte) 0x65, (byte) 0x73, (byte) 0x53, + (byte) 0x44, (byte) 0x49, (byte) 0x60, (byte) 0x00, (byte) 0x06 }; + + // private static final byte[] C_CV_IFD = new byte[] { + // + // (byte)0x7F, (byte)0x21, (byte)0x81, (byte)0xCD, (byte)0x5F, (byte)0x37, + // (byte)0x81, (byte)0x80, (byte)0x25, (byte)0x63, (byte)0xFC, (byte)0xF6, + // (byte)0x71, (byte)0x46, (byte)0x24, (byte)0xC0, (byte)0xC4, (byte)0xF5, + // (byte)0xD7, (byte)0x5F, (byte)0x67, (byte)0x30, (byte)0x5E, (byte)0xBF, + // (byte)0x8F, (byte)0x3A, (byte)0x6B, (byte)0xB7, (byte)0x72, (byte)0x99, + // (byte)0x6A, (byte)0xFB, (byte)0x64, (byte)0x97, (byte)0xBB, (byte)0x46, + // (byte)0x6A, (byte)0x23, (byte)0x8E, (byte)0x73, (byte)0x1D, (byte)0x08, + // (byte)0xD3, (byte)0x78, (byte)0xE9, (byte)0xF4, (byte)0xEC, (byte)0xC0, + // (byte)0x40, (byte)0x70, (byte)0xE3, (byte)0x71, (byte)0x7C, (byte)0x13, + // (byte)0x8E, (byte)0xA8, (byte)0xD6, (byte)0xD3, (byte)0x5A, (byte)0x14, + // (byte)0xED, (byte)0x18, (byte)0x62, (byte)0xB7, (byte)0xF8, (byte)0x4E, + // (byte)0x35, (byte)0x1B, (byte)0x2D, (byte)0xCE, (byte)0x4C, (byte)0xFB, + // (byte)0x30, (byte)0xF8, (byte)0xC7, (byte)0x6B, (byte)0x8A, (byte)0xD1, + // (byte)0x73, (byte)0x1E, (byte)0x9A, (byte)0xA8, (byte)0x4A, (byte)0xB0, + // (byte)0xB3, (byte)0xBD, (byte)0x30, (byte)0xC3, (byte)0xF0, (byte)0x0D, + // (byte)0xA2, (byte)0x74, (byte)0xE2, (byte)0x00, (byte)0x5A, (byte)0x51, + // (byte)0xEB, (byte)0x42, (byte)0x13, (byte)0xFD, (byte)0x55, (byte)0x23, + // (byte)0xAB, (byte)0xC9, (byte)0x75, (byte)0x84, (byte)0xA9, (byte)0xFB, + // (byte)0xD2, (byte)0x57, (byte)0x6C, (byte)0xB5, (byte)0xDD, (byte)0x9D, + // (byte)0xD5, (byte)0x72, (byte)0xAE, (byte)0x49, (byte)0xA5, (byte)0x97, + // (byte)0xE1, (byte)0x4E, (byte)0xCF, (byte)0xFA, (byte)0x91, (byte)0xF7, + // (byte)0x6E, (byte)0x04, (byte)0xF6, (byte)0x08, (byte)0x12, (byte)0x92, + // (byte)0xAE, (byte)0x07, (byte)0xCE, (byte)0xF7, (byte)0x5F, (byte)0x38, + // (byte)0x3C, (byte)0x25, (byte)0x8B, (byte)0x73, (byte)0xCF, (byte)0xB9, + // (byte)0x4A, (byte)0x73, (byte)0x9C, (byte)0xB5, (byte)0xE9, (byte)0x73, + // (byte)0x92, (byte)0xE5, (byte)0x99, (byte)0xE8, (byte)0xFB, (byte)0x45, + // (byte)0xA6, (byte)0x00, (byte)0x72, (byte)0xCA, (byte)0xA6, (byte)0xFC, + // (byte)0xD5, (byte)0xF2, (byte)0x15, (byte)0xC3, (byte)0xC7, (byte)0xE0, + // (byte)0x25, (byte)0xEA, (byte)0x3C, (byte)0x9E, (byte)0xB2, (byte)0xCB, + // (byte)0xE4, (byte)0x7D, (byte)0xE9, (byte)0xFE, (byte)0xE8, (byte)0x00, + // (byte)0x2B, (byte)0xF2, (byte)0xF6, (byte)0xD4, (byte)0xA2, (byte)0x43, + // (byte)0x50, (byte)0xAB, (byte)0x3F, (byte)0x5F, (byte)0x15, (byte)0xDB, + // (byte)0x05, (byte)0xA1, (byte)0x27, (byte)0x00, (byte)0x01, (byte)0x00, + // (byte)0x01, (byte)0x42, (byte)0x08, (byte)0x65, (byte)0x73, (byte)0x53, + // (byte)0x44, (byte)0x49, (byte)0x60, (byte)0x00, (byte)0x06 + // }; + + private static final byte[] KEY_SELECTOR = new byte[] { + + (byte) 0x83, (byte) 0x0C, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x84, + (byte) 0x02, (byte) 0x02, (byte) 0x1F }; + + private static final byte[] C_CV_IFD = new byte[] { + + (byte) 0x7f, (byte) 0x21, (byte) 0x81, (byte) 0xcd, (byte) 0x5f, + (byte) 0x37, (byte) 0x81, (byte) 0x80, (byte) 0x82, (byte) 0x5b, + (byte) 0x69, (byte) 0xc6, (byte) 0x45, (byte) 0x1e, (byte) 0x5f, + (byte) 0x51, (byte) 0x70, (byte) 0x74, (byte) 0x38, (byte) 0x5f, + (byte) 0x2f, (byte) 0x17, (byte) 0xd6, (byte) 0x4d, (byte) 0xfe, + (byte) 0x2e, (byte) 0x68, (byte) 0x56, (byte) 0x75, (byte) 0x67, + (byte) 0x09, (byte) 0x4b, (byte) 0x57, (byte) 0xf3, (byte) 0xc5, + (byte) 0x78, (byte) 0xe8, (byte) 0x30, (byte) 0xe4, (byte) 0x25, + (byte) 0x57, (byte) 0x2d, (byte) 0xe8, (byte) 0x28, (byte) 0xfa, + (byte) 0xf4, (byte) 0xde, (byte) 0x1b, (byte) 0x01, (byte) 0xc3, + (byte) 0x94, (byte) 0xe3, (byte) 0x45, (byte) 0xc2, (byte) 0xfb, + (byte) 0x06, (byte) 0x29, (byte) 0xa3, (byte) 0x93, (byte) 0x49, + (byte) 0x2f, (byte) 0x94, (byte) 0xf5, (byte) 0x70, (byte) 0xb0, + (byte) 0x0b, (byte) 0x1d, (byte) 0x67, (byte) 0x77, (byte) 0x29, + (byte) 0xf7, (byte) 0x55, (byte) 0xd1, (byte) 0x07, (byte) 0x02, + (byte) 0x2b, (byte) 0xb0, (byte) 0xa1, (byte) 0x16, (byte) 0xe1, + (byte) 0xd7, (byte) 0xd7, (byte) 0x65, (byte) 0x9d, (byte) 0xb5, + (byte) 0xc4, (byte) 0xac, (byte) 0x0d, (byte) 0xde, (byte) 0xab, + (byte) 0x07, (byte) 0xff, (byte) 0x04, (byte) 0x5f, (byte) 0x37, + (byte) 0xb5, (byte) 0xda, (byte) 0xf1, (byte) 0x73, (byte) 0x2b, + (byte) 0x54, (byte) 0xea, (byte) 0xb2, (byte) 0x38, (byte) 0xa2, + (byte) 0xce, (byte) 0x17, (byte) 0xc9, (byte) 0x79, (byte) 0x41, + (byte) 0x87, (byte) 0x75, (byte) 0x9c, (byte) 0xea, (byte) 0x9f, + (byte) 0x92, (byte) 0xa1, (byte) 0x78, (byte) 0x05, (byte) 0xa2, + (byte) 0x7c, (byte) 0x10, (byte) 0x15, (byte) 0xec, (byte) 0x56, + (byte) 0xcc, (byte) 0x7e, (byte) 0x47, (byte) 0x1a, (byte) 0x48, + (byte) 0x8e, (byte) 0x6f, (byte) 0x1b, (byte) 0x91, (byte) 0xf7, + (byte) 0xaa, (byte) 0x5f, (byte) 0x38, (byte) 0x3c, (byte) 0xad, + (byte) 0xfc, (byte) 0x12, (byte) 0xe8, (byte) 0x56, (byte) 0xb2, + (byte) 0x02, (byte) 0x34, (byte) 0x6a, (byte) 0xf8, (byte) 0x22, + (byte) 0x6b, (byte) 0x1a, (byte) 0x88, (byte) 0x21, (byte) 0x37, + (byte) 0xdc, (byte) 0x3c, (byte) 0x5a, (byte) 0x57, (byte) 0xf0, + (byte) 0xd2, (byte) 0x81, (byte) 0x5c, (byte) 0x1f, (byte) 0xcd, + (byte) 0x4b, (byte) 0xb4, (byte) 0x6f, (byte) 0xa9, (byte) 0x15, + (byte) 0x7f, (byte) 0xdf, (byte) 0xfd, (byte) 0x79, (byte) 0xec, + (byte) 0x3a, (byte) 0x10, (byte) 0xa8, (byte) 0x24, (byte) 0xcc, + (byte) 0xc1, (byte) 0xeb, (byte) 0x3c, (byte) 0xe0, (byte) 0xb6, + (byte) 0xb4, (byte) 0x39, (byte) 0x6a, (byte) 0xe2, (byte) 0x36, + (byte) 0x59, (byte) 0x00, (byte) 0x16, (byte) 0xba, (byte) 0x69, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x42, + (byte) 0x08, (byte) 0x65, (byte) 0x73, (byte) 0x53, (byte) 0x44, + (byte) 0x49, (byte) 0x60, (byte) 0x00, (byte) 0x06 + + }; + + private static final byte[] RANDOM_TAIL = new byte[] { + + (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x01 }; + + private static final byte[] KENC_COMPUTATION_TAIL = new byte[] { + + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01 }; + + private static final byte[] KMAC_COMPUTATION_TAIL = new byte[] { + + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02 }; + + // private static final byte[] TEST_CHALLENGE = new byte[] { + // + // (byte)0xfe, (byte)0x65, (byte)0x72, (byte)0x83, (byte)0xd6, (byte)0x14, + // (byte)0xc5, (byte)0x4e + // }; + // + // private static final byte[] TEST_RESPONSE = new byte[] { + // + // (byte)0xce, (byte)0x19, (byte)0x59, (byte)0x34, (byte)0x09, (byte)0xe0, + // (byte)0xf4, (byte)0x06, (byte)0xa0, (byte)0x95, (byte)0x45, (byte)0x7c, + // (byte)0x81, (byte)0x2a, (byte)0x07, (byte)0x7d, (byte)0x1c, (byte)0xc3, + // (byte)0xcf, (byte)0x46, (byte)0x15, (byte)0xc9, (byte)0x39, (byte)0x8b, + // (byte)0x16, (byte)0x1b, (byte)0x06, (byte)0x50, (byte)0xbb, (byte)0xc1, + // (byte)0x60, (byte)0x65, (byte)0xc4, (byte)0xa3, (byte)0xad, (byte)0xcd, + // (byte)0x10, (byte)0x26, (byte)0x51, (byte)0xd0, (byte)0x1e, (byte)0x23, + // (byte)0x80, (byte)0x57, (byte)0xd6, (byte)0xae, (byte)0x8a, (byte)0xb4, + // (byte)0xd8, (byte)0xb8, (byte)0xbb, (byte)0x68, (byte)0x0d, (byte)0x6f, + // (byte)0xae, (byte)0xf1, (byte)0xef, (byte)0x6b, (byte)0x5b, (byte)0x20, + // (byte)0xd8, (byte)0x45, (byte)0x26, (byte)0xb3, (byte)0x39, (byte)0x9f, + // (byte)0xd5, (byte)0xad, (byte)0x0d, (byte)0x7d, (byte)0x3b, (byte)0xac, + // (byte)0x7f, (byte)0x11, (byte)0x2f, (byte)0xee, (byte)0xc4, (byte)0xe8, + // (byte)0x4a, (byte)0xd5, (byte)0xd0, (byte)0x55, (byte)0x3f, (byte)0xe3, + // (byte)0x1b, (byte)0xaf, (byte)0xd9, (byte)0x93, (byte)0x7f, (byte)0xa5, + // (byte)0xa0, (byte)0x4f, (byte)0x9f, (byte)0xb4, (byte)0xa0, (byte)0x65, + // (byte)0xc6, (byte)0x4a, (byte)0xc9, (byte)0xf4, (byte)0xf7, (byte)0x09, + // (byte)0x9a , (byte)0x56, (byte)0x3f, (byte)0xb3, (byte)0xdb, (byte)0xa3, + // (byte)0x96, (byte)0x10, (byte)0xd5, (byte)0xae, (byte)0x70, (byte)0x56, + // (byte)0xc5, (byte)0x8c, (byte)0x32, (byte)0x44, (byte)0x19, (byte)0x2a, + // (byte)0x62, (byte)0xae, (byte)0xa9, (byte)0x12, (byte)0xf6, (byte)0x86, + // (byte)0x3e, (byte)0x76 + // + // }; + // + // private static final byte[] TEST_RESPONSE_DOCU = new byte[] { + // + // (byte)0x43,(byte)0x73,(byte)0x93,(byte)0xb3,(byte)0x0d,(byte)0x1f,(byte)0x01,(byte)0x61,(byte)0x45,(byte)0x5b,(byte)0xb1,(byte)0x32,(byte)0xe9,(byte)0x99,(byte)0xde,(byte)0x7b,(byte)0xb8,(byte)0xf7,(byte)0xd8,(byte)0x2f,(byte)0x91,(byte)0xd5,(byte)0x07,(byte)0xd6 + // ,(byte)0xd1,(byte)0x16,(byte)0x07,(byte)0x4d,(byte)0x33,(byte)0xe6,(byte)0x04,(byte)0x57,(byte)0xf1,(byte)0x89,(byte)0xb9,(byte)0x76,(byte)0x23,(byte)0x5c,(byte)0xab,(byte)0x57,(byte)0x62,(byte)0xb6,(byte)0x4f,(byte)0x89,(byte)0x6b,(byte)0xe8,(byte)0xa9,(byte)0x24 + // ,(byte)0x1a,(byte)0x24,(byte)0x5d,(byte)0xca,(byte)0xc9,(byte)0x76,(byte)0xfa,(byte)0x2d,(byte)0x0c,(byte)0xac,(byte)0x87,(byte)0x19,(byte)0x15,(byte)0x7e,(byte)0x29,(byte)0x27,(byte)0xc6,(byte)0x1e,(byte)0x0b,(byte)0xcb,(byte)0x48,(byte)0xb5,(byte)0x11,(byte)0x70 + // ,(byte)0xea,(byte)0x08,(byte)0x98,(byte)0x38,(byte)0x1e,(byte)0xf9,(byte)0x19,(byte)0x39,(byte)0x8e,(byte)0x46,(byte)0x41,(byte)0x78,(byte)0x99,(byte)0xab,(byte)0xe8,(byte)0x27,(byte)0x08,(byte)0xad,(byte)0xdd,(byte)0x1b,(byte)0x75,(byte)0x5c,(byte)0x05,(byte)0x6f + // ,(byte)0x5f,(byte)0x7b,(byte)0x96,(byte)0xba,(byte)0x69,(byte)0xbd,(byte)0x56,(byte)0xfc,(byte)0x57,(byte)0x6c,(byte)0x80,(byte)0x9b,(byte)0x27,(byte)0xb8,(byte)0xf8,(byte)0x36,(byte)0x4f,(byte)0xc4,(byte)0xd5,(byte)0x59,(byte)0xd1,(byte)0xda,(byte)0x86,(byte)0x81 + // ,(byte)0xfa,(byte)0x04,(byte)0x14,(byte)0x5d,(byte)0xdd,(byte)0x63,(byte)0x6e,(byte)0xb7 + // }; + // + // private static final byte[] TEST_CHALLENGE_2 = new byte[] { + // + // (byte)0x15, (byte)0x5A, (byte)0xA9, (byte)0x3F, (byte)0xD5, (byte)0xE3, + // (byte)0xBC, (byte)0xA3 + // }; + // + // private static final byte[] TEST_RESPONSE_2 = new byte[] { + // + // (byte)0x0d,(byte)0x82,(byte)0xd5 ,(byte)0xbb ,(byte)0x84 ,(byte)0x00 + // ,(byte)0x2b ,(byte)0x2a ,(byte)0xed ,(byte)0x57 ,(byte)0x6e ,(byte)0x63 + // ,(byte)0x6f ,(byte)0xf3 ,(byte)0xc6 ,(byte)0x1a ,(byte)0xde ,(byte)0xb6 + // ,(byte)0x28 ,(byte)0x6b ,(byte)0x23 ,(byte)0x9c ,(byte)0xfb ,(byte)0x7c + // ,(byte)0x65 ,(byte)0xce ,(byte)0x6e ,(byte)0x1a ,(byte)0x49 ,(byte)0x57 + // ,(byte)0x47 ,(byte)0x41 ,(byte)0xb1 ,(byte)0x7e ,(byte)0x85 ,(byte)0x10 + // ,(byte)0xaa ,(byte)0x0f ,(byte)0xef ,(byte)0x44 ,(byte)0x89 ,(byte)0x7a + // ,(byte)0xcd ,(byte)0xfc ,(byte)0xbb ,(byte)0x33 ,(byte)0x9a ,(byte)0x7e + // ,(byte)0xaf ,(byte)0x2c ,(byte)0xa2 ,(byte)0x46 ,(byte)0xb3 ,(byte)0xba + // ,(byte)0x8f ,(byte)0x7c ,(byte)0x16 ,(byte)0x3a ,(byte)0x36 ,(byte)0xf1 + // ,(byte)0xbc ,(byte)0x89 ,(byte)0x53 ,(byte)0x9d ,(byte)0x74 ,(byte)0x72 + // ,(byte)0x21 ,(byte)0xb0 ,(byte)0xb2 ,(byte)0xd9 ,(byte)0xa3 ,(byte)0xc4 + // ,(byte)0xa0 ,(byte)0xa2 ,(byte)0xd8 ,(byte)0x2b ,(byte)0x30 ,(byte)0x4f + // ,(byte)0xbd ,(byte)0x6f ,(byte)0xc3 ,(byte)0xfc ,(byte)0xd4 ,(byte)0x2d + // ,(byte)0xea ,(byte)0x1d ,(byte)0x08 ,(byte)0xad ,(byte)0x1c ,(byte)0x98 + // ,(byte)0x51 ,(byte)0xb3 ,(byte)0x0f ,(byte)0xbf ,(byte)0x11 ,(byte)0x6e + // ,(byte)0x78 ,(byte)0x0b ,(byte)0xb4 ,(byte)0x0f ,(byte)0x1f ,(byte)0x55 + // ,(byte)0xc7 ,(byte)0xc9 ,(byte)0x75 ,(byte)0xcb ,(byte)0xe3 ,(byte)0x36 + // ,(byte)0x17 ,(byte)0x63 ,(byte)0xe4 ,(byte)0xed ,(byte)0x8d ,(byte)0xe4 + // ,(byte)0xaa ,(byte)0x80 ,(byte)0xf6 ,(byte)0xd7 ,(byte)0x73 ,(byte)0x0a + // ,(byte)0xc1 ,(byte)0xdf ,(byte)0xa9 ,(byte)0x89 ,(byte)0x02 ,(byte)0xc7 + // ,(byte)0x2b ,(byte)0xa8 + // }; + // + // private static final byte[] TEST_CARD_CHALLENGE = new byte[] { + // + // (byte)0xea,(byte)0xef,(byte)0xa8,(byte)0xfb,(byte)0xd3,(byte)0x1a,(byte)0xc8,(byte)0xec + // }; + // + // private static final byte[] TEST_PRND2 = new byte[] { + // + // (byte)0x2a,(byte)0x5c,(byte)0x73,(byte)0x00,(byte)0xd9,(byte)0x6d,(byte)0xf9,(byte)0x19,(byte)0x0e,(byte)0xc2,(byte)0xae,(byte)0x1c,(byte)0xef,(byte)0x89,(byte)0xbd,(byte)0xe1,(byte)0x56,(byte)0x78,(byte)0xa7,(byte)0x8b,(byte)0x7f,(byte)0x7d,(byte)0x4d,(byte)0xf3 + // ,(byte)0xd7,(byte)0xd9,(byte)0xbc,(byte)0xfa,(byte)0x31,(byte)0x3a,(byte)0x09,(byte)0x86,(byte)0xef,(byte)0xcc,(byte)0x39,(byte)0x69,(byte)0x5b,(byte)0xd2,(byte)0x98,(byte)0x12,(byte)0xd2,(byte)0xce,(byte)0x59,(byte)0x31,(byte)0xe1,(byte)0xc2,(byte)0xfc,(byte)0x2d + // ,(byte)0x06,(byte)0x8a,(byte)0x5c,(byte)0xaf,(byte)0x3d,(byte)0xe5,(byte)0xba,(byte)0xfe,(byte)0xf6,(byte)0x21,(byte)0xcd,(byte)0x8b,(byte)0xa7,(byte)0x08,(byte)0x26,(byte)0x59,(byte)0xa3,(byte)0x07,(byte)0xd9,(byte)0xa4,(byte)0x53,(byte)0x18,(byte)0xd4,(byte)0x79 + // ,(byte)0x80,(byte)0x84 + // }; + // + // private static final byte[] TEST_KIFD = new byte[] { + // + // (byte)0xcd,(byte)0x97,(byte)0xdb,(byte)0xf2,(byte)0x23,(byte)0x57,(byte)0x60,(byte)0xd8,(byte)0xb3,(byte)0xe9,(byte)0xc4,(byte)0xd7,(byte)0xe9,(byte)0xa6,(byte)0x91,(byte)0xfb,(byte)0x12,(byte)0xcd,(byte)0x20,(byte)0x7c,(byte)0x42,(byte)0x66,(byte)0x0d,(byte)0xf9 + // ,(byte)0xc6,(byte)0x93,(byte)0x57,(byte)0xba,(byte)0x7b,(byte)0x25,(byte)0xd0,(byte)0xfc + // }; + + private static final byte[] IV = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + private int prndLength; + private byte[] snIcc; + private byte[] componentCert; + private byte[] intermediateCert; + + private byte[] kicc; + private byte[] kifd; + + private byte[] kEnc; + private byte[] kMac; + + private byte[] rndIcc; + private byte[] rndIfd; + private byte[] ssc; + + private byte[] sigVal; + private byte[] sigCert; + + /** + * @param args + */ + public static void main(String[] args) { + + System.setProperty("sun.security.smartcardio.t0GetResponse", "false"); + +// IAIK.addAsProvider(); + + ESCardTest tester = new ESCardTest(); + + try { + +// createRSAPrivateKey(TERMINAL_MODULO, TERMINAL_PRIVEXP); + tester.establishSecureChannel(); +// tester.testSigVerify(); +// tester.testZLib(); + + } catch (Exception e) { + e.printStackTrace(); + } + + } + + private void testZLib() { + + try { + // Encode a String into bytes + String inputString = "blahblahblah??"; + byte[] input = inputString.getBytes("UTF-8"); + + // Compress the bytes + byte[] output = new byte[100]; + Deflater compresser = new Deflater(); + compresser.setInput(input); + compresser.finish(); + int compressedDataLength = compresser.deflate(output); + + System.out.println("compressed data (len):" + compressedDataLength); + printByteArray(output); + + // Decompress the bytes + Inflater decompresser = new Inflater(); + decompresser.setInput(output, 0, compressedDataLength); + byte[] result = new byte[100]; + int resultLength = decompresser.inflate(result); + decompresser.end(); + + System.out.println("Decompressed data (len):" + resultLength); + printByteArray(result); + + // Decode the bytes into a String + String outputString = new String(result, 0, resultLength, "UTF-8"); + } catch(java.io.UnsupportedEncodingException ex) { + // handle + } catch (java.util.zip.DataFormatException ex) { + // handle + } + + + } + + private byte[] decompressData(byte[] input, int len) throws CardException { + + Inflater decompresser = new Inflater(); + decompresser.setInput(input, 0, input.length); + byte[] result = new byte[len]; + + try { + decompresser.inflate(result); + decompresser.end(); + + return result; + + } catch (DataFormatException e) { + + throw new CardException("Error decompressing file." , e); + } + + } + + private void testSigVerify() throws CardException { + + this.kEnc = new byte[] { + + (byte)0x59, (byte)0x8f, (byte)0x26, (byte)0xe3, (byte)0x6e, (byte)0x11, (byte)0xa8, (byte)0xec, (byte)0x14, (byte)0xb8, (byte)0x1e, (byte)0x19, (byte)0xbd, (byte)0xa2, (byte)0x23, (byte)0xca + }; + + this.kMac = new byte[] { + + (byte)0x5d, (byte)0xe2, (byte)0x93, (byte)0x9a, (byte)0x1e, (byte)0xa0, (byte)0x3a, (byte)0x93, (byte)0x0b, (byte)0x88, (byte)0x20, (byte)0x6d, (byte)0x8f, (byte)0x73, (byte)0xe8, (byte)0xa7 + }; + + this.ssc = new byte[] { + + (byte)0xd3, (byte)0x1a, (byte)0xc8, (byte)0xec, (byte)0x7b, (byte)0xa0, (byte)0xfe, (byte)0x73 + }; + + byte[] sigData_1 = new byte[] { + + (byte)0x87, (byte)0x82, (byte)0x01, (byte)0x09, (byte)0x01, (byte)0xe4, (byte)0xe9, (byte)0xca, (byte)0x20, (byte)0xeb, (byte)0x99, (byte)0x6f, (byte)0x14, (byte)0xf2, (byte)0x19, (byte)0xd0, (byte)0x86, (byte)0xb1, (byte)0x31, (byte)0x87, (byte)0xd6, (byte)0x40, (byte)0xa7, (byte)0xf7 + , (byte)0xd9, (byte)0xa9, (byte)0x3b, (byte)0x74, (byte)0x36, (byte)0x88, (byte)0x70, (byte)0xe7, (byte)0x30, (byte)0x0e, (byte)0xc5, (byte)0xdb, (byte)0x20, (byte)0x2e, (byte)0x68, (byte)0x3e, (byte)0x85, (byte)0x07, (byte)0xae, (byte)0x87, (byte)0x1f, (byte)0xf9, (byte)0xfe, (byte)0x6c + , (byte)0x10, (byte)0xcd, (byte)0x6b, (byte)0x66, (byte)0xe9, (byte)0x42, (byte)0xaf, (byte)0xfa, (byte)0xc1, (byte)0x99, (byte)0x6a, (byte)0x20, (byte)0x48, (byte)0xe0, (byte)0x56, (byte)0x4d, (byte)0x8e, (byte)0xdd, (byte)0x8b, (byte)0x00, (byte)0x57, (byte)0xea, (byte)0xa0, (byte)0x57 + , (byte)0x6e, (byte)0xc8, (byte)0x3e, (byte)0x0b, (byte)0x62, (byte)0xeb, (byte)0x07, (byte)0xbe, (byte)0xa6, (byte)0xf7, (byte)0x4a, (byte)0x43, (byte)0xa8, (byte)0xd4, (byte)0xa5, (byte)0x18, (byte)0xf5, (byte)0x5d, (byte)0xae, (byte)0xba, (byte)0x0b, (byte)0xc6, (byte)0xa8, (byte)0x7f + , (byte)0x0e, (byte)0x30, (byte)0xfb, (byte)0x1a, (byte)0x75, (byte)0xd4, (byte)0x47, (byte)0x21, (byte)0x08, (byte)0xec, (byte)0xb0, (byte)0x43, (byte)0xdc, (byte)0xd2, (byte)0xf2, (byte)0x2f, (byte)0x85, (byte)0xdb, (byte)0x56, (byte)0x40, (byte)0xc8, (byte)0x6b, (byte)0x50, (byte)0x0b + , (byte)0xf5, (byte)0xa8, (byte)0x19, (byte)0xac, (byte)0x73, (byte)0x63, (byte)0xd8, (byte)0x79, (byte)0xf9, (byte)0x8b, (byte)0x42, (byte)0x95, (byte)0x35, (byte)0x9d, (byte)0x2f, (byte)0x7c, (byte)0x04, (byte)0xf1, (byte)0x34, (byte)0xfd, (byte)0x28, (byte)0xaf, (byte)0x07, (byte)0xb7 + , (byte)0xe5, (byte)0xb3, (byte)0x27, (byte)0xf3, (byte)0x21, (byte)0x4a, (byte)0x81, (byte)0x6f, (byte)0xcd, (byte)0xa4, (byte)0x89, (byte)0xb1, (byte)0x7e, (byte)0x0a, (byte)0x0f, (byte)0x9b, (byte)0x9c, (byte)0x3a, (byte)0xf5, (byte)0xb7, (byte)0xce, (byte)0xaa, (byte)0x43, (byte)0x11 + , (byte)0xc7, (byte)0xa6, (byte)0x6e, (byte)0xec, (byte)0xe2, (byte)0x10, (byte)0xde, (byte)0x9b, (byte)0x47, (byte)0x1c, (byte)0x78, (byte)0xae, (byte)0xf8, (byte)0x58, (byte)0xcf, (byte)0xea, (byte)0xeb, (byte)0x11, (byte)0xda, (byte)0x40, (byte)0xba, (byte)0x93, (byte)0x2d, (byte)0x49 + , (byte)0x90, (byte)0x0b, (byte)0x73, (byte)0xf6, (byte)0xa1, (byte)0x44, (byte)0x51, (byte)0xc0, (byte)0xca, (byte)0xe0, (byte)0x4b, (byte)0x08, (byte)0x5b, (byte)0x62, (byte)0xaa, (byte)0x19, (byte)0x03, (byte)0x7b, (byte)0x62, (byte)0xea, (byte)0xf7, (byte)0xb9, (byte)0x7b, (byte)0xf7 + , (byte)0xe6, (byte)0xdf, (byte)0x54, (byte)0xcb, (byte)0x8a, (byte)0x4e, (byte)0x3d, (byte)0xb5, (byte)0xbc, (byte)0x56, (byte)0x81, (byte)0x84, (byte)0x67, (byte)0xac, (byte)0x75, (byte)0x6b, (byte)0x53, (byte)0xba, (byte)0x04, (byte)0xb5, (byte)0xdb, (byte)0x0f, (byte)0x55, (byte)0xc1 + , (byte)0xdb, (byte)0x23, (byte)0xf2, (byte)0x28, (byte)0x9f, (byte)0x9e, (byte)0x2c, (byte)0x35, (byte)0x96, (byte)0x71, (byte)0xba, (byte)0x17, (byte)0x4a, (byte)0x9e, (byte)0xcd, (byte)0xe2, (byte)0x61, (byte)0x17 + }; + + byte[] sigData_2 = new byte[] { + + (byte)0x78, (byte)0x4d, (byte)0xd3, (byte)0x60, (byte)0x9f, (byte)0x32, (byte)0xf0, (byte)0x40, (byte)0xda, (byte)0xe1, (byte)0xc1, (byte)0x44, (byte)0x7e, (byte)0x99, (byte)0x02, (byte)0x90, (byte)0x00, (byte)0x8e, (byte)0x04, (byte)0x99, (byte)0x9b, (byte)0x1e, (byte)0x4e, (byte)0x90, (byte)0x00 + + }; + + //------------------ + + System.out.println("Data_1 lenght: " + sigData_1.length); + System.out.println("Data_2 lenght: " + sigData_2.length); + + byte[] allData = new byte[sigData_1.length-2 + sigData_2.length-2]; + System.arraycopy(sigData_1, 0, allData, 0, sigData_1.length-2); + System.arraycopy(sigData_2, 0, allData, sigData_1.length-2, sigData_2.length-2); + + System.out.println("AllData lenght: " + allData.length); + System.out.println("AllData:"); + printByteArray(allData); + + verifyAndDecryptSecuredResponseAPDU(allData); + + + + } + + private byte[] executeGetResponse(CardChannel channel, byte sw2) throws CardException { + +// System.out.print("Run GET RESPONSE.."); + + boolean done=false; + ByteArrayOutputStream bof = new ByteArrayOutputStream(); + + while(!done) { + + CommandAPDU command = new CommandAPDU(new byte[] { (byte) 0x00, (byte) 0xC0, + (byte) 0x00, (byte) 0x00, (byte) sw2 }); + ResponseAPDU resp = channel.transmit(command); + + // System.out.println("Answer from card: " + Integer.toHexString(resp.getSW())); + // System.out.println("Number of bytes read: " + resp.getData().length); + // +// if(resp.getSW1() == (byte)0x61 || resp.getSW() == 0x9000) { +// +// return resp.getData(); +// } else { +// +// throw new CardException("Command GET RESPONSE failed. SW=" + Integer.toHexString(resp.getSW())); +// } + +// System.out.println(Integer.toHexString(resp.getSW())); + + try { + bof.write(resp.getData()); + } catch (IOException e) { + + throw new CardException("Unable to read card response.", e); + } + + if(resp.getSW1() == (byte)0x61) { + + // more data to be read + sw2 = (byte)resp.getSW2(); + continue; + } + + if(resp.getSW() == 0x9000) { + + // all data read + done=true; + } else { + + throw new CardException("An error has occured while fetching response from card: " + Integer.toHexString(resp.getSW())); + } + + } + + return bof.toByteArray(); + } + + public void testT0() throws CardException { + + CardChannel channel = setupCardChannel(); + + byte[] apdu = new byte[] { (byte) 0x00, (byte) 0xA4, (byte) 0x00, + (byte) 0x00, (byte) 0x02, (byte)0x60, (byte)0x20 }; + CommandAPDU command = new CommandAPDU(apdu); + ResponseAPDU resp = channel.transmit(command); + + System.out.println("Response: " + Integer.toHexString(resp.getSW())); + + + } + + private void executeSecureSelectCertificate(CardChannel channel) throws CardException { + + // select master file + executeSecureSelectMasterFile(channel); + + // select 6081 + byte[] apdu = new byte[]{ + + (byte)0x00, + (byte)0xA4, + (byte)0x00, + (byte)0x00, + (byte)0x02, + (byte)0x60, + (byte)0x81 + }; + + executeSecureSelect(channel, apdu); + + // select 7004 + byte[] apdu2 = new byte[]{ + + (byte)0x00, + (byte)0xA4, + (byte)0x00, + (byte)0x00, + (byte)0x02, + (byte)0x70, + (byte)0x04 + }; + + byte[] fci = executeSecureSelect(channel, apdu2); + +// System.out.println("Obtained FCI:"); +// printByteArray(fci); + + byte sizeHi = fci[7]; + byte sizeLo = fci[8]; + + byte[] data = executeSecureReadBinary(channel, sizeHi, sizeLo); + + int uncompressedDataLen = getUncompressedDataLength(data); + + byte[] compressedWithoutHeader = new byte[data.length-8]; + System.arraycopy(data, 8, compressedWithoutHeader, 0, compressedWithoutHeader.length); + + byte[] decompressed = decompressData(compressedWithoutHeader, uncompressedDataLen); + + writeDataToFile(decompressed, "F:/cert_7004.cer"); + + this.sigCert = decompressed; + + } + + private X509Certificate createCertificate(byte[] certData) throws CardException { + + try { + InputStream inStream = new ByteArrayInputStream(certData); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + X509Certificate certificate = (X509Certificate)cf.generateCertificate(inStream); + inStream.close(); + + return certificate; + + }catch(Exception e) { + + throw new CardException("Unable to create certificate.", e); + } + + + } + + private void verifySignature() throws CardException { + + PublicKey pubKey = null; + + + // IAIK: +// X509Certificate certificate = new X509Certificate(this.sigCert); + + // SUN: + X509Certificate certificate = createCertificate(this.sigCert); + + pubKey = certificate.getPublicKey(); + + + try { + byte[] plain = rsaDecrypt(pubKey, this.sigVal); + + System.out.println("Deciphered Data:"); + printByteArray(plain); + + } catch (InvalidKeyException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (NoSuchAlgorithmException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (NoSuchPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalBlockSizeException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (BadPaddingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + private int getUncompressedDataLength(byte[] data) { + + byte len0 = data[0]; + byte len1 = data[1]; + byte len2 = data[2]; + byte len3 = data[3]; + + int a = len0; + int b = len1 * 256; + int c = len2 * 256 * 256; + int d = len3 * 256 * 256 * 256; + + return a+b+c+d; + + } + + private void executeSecurePerformSecurityOperation(CardChannel channel) throws CardException { + + System.out.print("Compute electronic signature on card.."); + + byte[] testHashData = new byte[] { + + (byte)0x00, (byte)0x11, (byte)0x22, (byte)0x33, (byte)0x44, (byte)0x55, (byte)0x66, (byte)0x77, (byte)0x88, (byte)0x99, (byte)0xAA, (byte)0xBB, (byte)0xCC, (byte)0xDD, (byte)0xEE, (byte)0xFF, (byte)0x01, (byte)0x02, (byte)0x03, (byte)0x04 + }; + + byte[] padding = new byte[] { + + (byte)0x30, (byte)0x21, (byte)0x30, (byte)0x09, (byte)0x06, (byte)0x05, (byte)0x2B, (byte)0x0E, (byte)0x03, (byte)0x02, (byte)0x1A, (byte)0x05, (byte)0x00, (byte)0x04, (byte)0x14 + }; + + byte[] apduHeader = new byte[] { + + (byte)0x00, (byte)0x2A, (byte)0x9E, (byte)0x9A, (byte)0x23 + }; + + byte[] apdu = new byte[apduHeader.length + padding.length + testHashData.length]; + System.arraycopy(apduHeader, 0, apdu, 0, apduHeader.length); + System.arraycopy(padding, 0, apdu, apduHeader.length, padding.length); + System.arraycopy(testHashData, 0, apdu, apduHeader.length+padding.length, testHashData.length); + + byte[] securedAPDU = getSecuredAPDU(apdu); + + CommandAPDU command = new CommandAPDU(securedAPDU); + ResponseAPDU resp = channel.transmit(command); + +// System.out.println("Response to SignCommand: " + Integer.toHexString(resp.getSW())); + + byte[] signatureValue = executeGetResponse(channel, (byte)resp.getSW2()); + +// System.out.println("Encrypted Signature Value:"); +// printByteArray(signatureValue); +// System.out.println("Length of encrypted signature value: " + signatureValue.length); + + + +// boolean done = false; +// ByteArrayOutputStream bof = new ByteArrayOutputStream(); + +// while(!done) { +// +// +// try { +// bof.write(resp.getData()); +// } catch (IOException e) { +// throw new CardException("Error reading response from card.", e); +// } +// +// } + + byte[] decryptedSignatureValueWithSW = verifyAndDecryptSecuredResponseAPDU(signatureValue); + + int len = decryptedSignatureValueWithSW.length; + if(decryptedSignatureValueWithSW[len-2] == (byte)0x90 && + decryptedSignatureValueWithSW[len-1] == (byte)0x00) { + + System.out.println("OK"); + + byte[] sigVal = new byte[decryptedSignatureValueWithSW.length-2]; + System.arraycopy(decryptedSignatureValueWithSW, 0, sigVal, 0, decryptedSignatureValueWithSW.length-2); + + System.out.println("Signature Value:"); + printByteArray(sigVal); + + this.sigVal = sigVal; + + } else { + + throw new CardException("Error creating signature on card: " + Integer.toHexString(decryptedSignatureValueWithSW[len-2]) + " " + Integer.toHexString(decryptedSignatureValueWithSW[len-1])); + } + + + + } + + private void executeSecureManageSecurityEnvironment(CardChannel channel) throws CardException { + + System.out.print("Manage Security Environment.."); + + byte[] apdu = new byte[] { + + (byte)0x00, (byte)0x22, (byte)0x41, (byte)0xB6, (byte)0x04, (byte)0x84, (byte)0x02, (byte)0x01, (byte)0x01 + }; + + byte[] securedAPDU = getSecuredAPDU(apdu); + + CommandAPDU command = new CommandAPDU(securedAPDU); + ResponseAPDU resp = channel.transmit(command); + +// System.out.println("Obtained result: " + Integer.toHexString(resp.getSW())); + + if(resp.getSW1() == (byte)0x61) { + + byte[] response = executeGetResponse(channel, (byte)resp.getSW2()); + byte[] decryptedResponse = verifyAndDecryptSecuredResponseAPDU(response); + +// System.out.println("Decrypted Response from card:"); +// printByteArray(decryptedResponse); + + if(decryptedResponse.length == 2 && + decryptedResponse[0] == (byte)0x90 && + decryptedResponse[1] == (byte)0x00) { + + System.out.println("OK"); + } else { + + System.out.println("FAILED"); + } + + } + + + + } + + private void executeSecureSelectMasterFile(CardChannel channel) throws CardException { + + byte[] apdu = new byte[] { + + (byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, (byte) 0x0B, + (byte) 0x4D, (byte) 0x61, (byte) 0x73, (byte) 0x74, + (byte) 0x65, (byte) 0x72, (byte) 0x2E, (byte) 0x46, + (byte) 0x69, (byte) 0x6C, (byte) 0x65 + + + }; + + executeSecureSelect(channel, apdu); + + } + + private void executeSecurePINVerify(CardChannel channel) throws CardException { + +// this.kEnc = new byte[] { +// +// (byte) 0x59, (byte) 0x8f, (byte) 0x26, (byte) 0xe3, (byte) 0x6e, +// (byte) 0x11, (byte) 0xa8, (byte) 0xec, (byte) 0x14, +// (byte) 0xb8, (byte) 0x1e, (byte) 0x19, (byte) 0xbd, +// (byte) 0xa2, (byte) 0x23, (byte) 0xca }; +// +// this.kMac = new byte[] { +// +// (byte) 0x5d, (byte) 0xe2, (byte) 0x93, (byte) 0x9a, (byte) 0x1e, +// (byte) 0xa0, (byte) 0x3a, (byte) 0x93, (byte) 0x0b, +// (byte) 0x88, (byte) 0x20, (byte) 0x6d, (byte) 0x8f, +// (byte) 0x73, (byte) 0xe8, (byte) 0xa7 }; +// +// this.ssc = new byte[] { +// +// (byte) 0xd3, (byte) 0x1a, (byte) 0xc8, (byte) 0xec, (byte) 0x7b, +// (byte) 0xa0, (byte) 0xfe, (byte) 0x6e }; +// + + + byte[] apdu = new byte[] { + + // PIN VERIFY (0 0 0 0 0 0 0 0) + (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x30, (byte)0x30, (byte)0x30, (byte)0x30, (byte)0x30, (byte)0x30, (byte)0x30, (byte)0x30 + + // PIN VERIFY (C R Y P T O K I) +// (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x43, (byte)0x52, (byte)0x59, (byte)0x50, (byte)0x54, (byte)0x4f, (byte)0x4b, (byte)0x49 + }; + + byte[] securedAPDU = getSecuredAPDU(apdu); + +// System.out.println("Secured PINVerify APDU:"); +// printByteArray(securedAPDU); + + System.out.print("Verifiying PIN.."); + + CommandAPDU command = new CommandAPDU(securedAPDU); +// System.out.println("Sending APDU: " + command.toString()); + ResponseAPDU resp = channel.transmit(command); + +// System.out.println("Received status word: " + Integer.toHexString(resp.getSW())); + + if(resp.getSW1() == (byte)0x61) { + +// System.out.println("Reading available bytes.."); + byte[] securedResponseData = executeGetResponse(channel, (byte)resp.getSW2()); +// printByteArray(securedResponseData); + + byte[] plainData = verifyAndDecryptSecuredResponseAPDU(securedResponseData); + +// System.out.println("Decrypted data:"); +// printByteArray(plainData); + + if(plainData.length == 2 && + plainData[0] == (byte)0x90 && + plainData[1] == (byte)0x00) { + + System.out.println("OK"); + } else { + + System.out.println("FAILED"); + } + + } + } + + private byte[] readFromCard(CardChannel channel, byte offsetHi, byte offsetLo, byte numBytes) throws CardException { + + byte[] apdu = new byte[] { + + (byte)0x00, (byte)0xB0, offsetHi, offsetLo, numBytes + }; + +// System.out.println("Sent APDU (plain):"); +// printByteArray(apdu); + + byte[] securedAPDU = getSecuredAPDU(apdu); + + + CommandAPDU command = new CommandAPDU(securedAPDU); + + ResponseAPDU resp = channel.transmit(command); +// System.out.println("Answer to read binary: " + Integer.toHexString(resp.getSW())); + + byte[] data = executeGetResponse(channel, (byte)resp.getSW2()); + + byte[] decryptedResponse = verifyAndDecryptSecuredResponseAPDU(data); + + return decryptedResponse; + + } + + private byte[] executeSecureReadBinary(CardChannel channel, byte lengthHi, byte lengthLo) throws CardException { + + System.out.print("Executing secure read binary.."); + + ByteArrayOutputStream bof = new ByteArrayOutputStream(); + + int bytes2read = (lengthHi * 256) + lengthLo; +// System.out.println("Bytes to read in total: " + bytes2read); + int bytesRead = 0; + + boolean done = false; + boolean forceExit = false; + + int offset = 0; + int len = 0; + + while (!done) { + +// System.out.println("\nEntering loop.."); +// System.out.println("Bytes read so far: " + bytesRead); + + if (bytes2read - bytesRead > 0xef) { + len = 0xef; + } else { + len = bytes2read - bytesRead; + } + +// System.out.println("Bytes to read in this iteration: " + len); + + + byte[] offsetBytes = intToHex(offset); + +// byte[] apdu = new byte[] { +// +// (byte)0x00, (byte)0xB0, offsetBytes[0], offsetBytes[1], (byte)len +// }; +// +// System.out.println("Sent APDU (plain):"); +// printByteArray(apdu); +// +// byte[] securedAPDU = getSecuredAPDU(apdu); +// +// +// CommandAPDU command = new CommandAPDU(securedAPDU); +// +// ResponseAPDU resp = channel.transmit(command); +// System.out.println("Answer to read binary: " + Integer.toHexString(resp.getSW())); +// +// byte[] data = executeGetResponse(channel, (byte)resp.getSW2()); +// +// byte[] decryptedResponse = verifyAndDecryptSecuredResponseAPDU(data); + + byte[] decryptedResponse = readFromCard(channel, offsetBytes[0], offsetBytes[1], (byte)len); + +// System.out.println("Decrypted response:"); +// printByteArray(decryptedResponse); + + if(decryptedResponse.length == 2 && + decryptedResponse[0] == (byte)0x6C) { + + // handle case: card returns 6CXX (wrong number of bytes requested) + // we assume that his only happens in the final iteration + + decryptedResponse = readFromCard(channel, offsetBytes[0], offsetBytes[1], decryptedResponse[1]); + +// System.out.println("Decrypted response:"); +// printByteArray(decryptedResponse); + + forceExit = true; + } + + + + byte[] decryptedData = new byte[decryptedResponse.length-2]; + System.arraycopy(decryptedResponse, 0, decryptedData, 0, decryptedResponse.length-2); + + try { + bof.write(decryptedData); + } catch (IOException e) { + throw new CardException("Error reading data from card", + e); + } + + bytesRead = bytesRead + decryptedData.length; + offset = bytesRead; + + if (bytesRead == bytes2read) { + + done = true; + } + + if(forceExit) { + + break; + } + } + + System.out.println("OK"); + + return bof.toByteArray(); + + } + + private void executeSecureReadPrKDF(CardChannel channel) throws CardException { + + System.out.println("Reading PrKDF.."); + + byte[] file = executeSecureReadFile(channel, new byte[]{(byte)0x50,(byte)0x15,(byte)0x60,(byte)0x01}); + writeDataToFile(file, "f:/PrKDF.bin"); + + System.out.println("Reading PrKDF successful."); + +// try { +// ASN1 asn1 = new ASN1(file); +// +// ASN1Object obj = asn1.toASN1Object(); +// +// System.out.println("Here!"); +// +// } catch (CodingException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } + + } + + private void executeSecureReadCDF(CardChannel channel) throws CardException { + + System.out.println("Reading CDF file.."); + + byte[] file = executeSecureReadFile(channel, new byte[]{(byte)0x50,(byte)0x15,(byte)0x60,(byte)0x04}); + writeDataToFile(file, "f:/CDF.bin"); + + System.out.println("Reading CDF file successful."); + } + + private byte[] executeSecureReadFile(CardChannel channel, byte[] path) throws CardException { + + System.out.println("Executing secure read File command.."); + + executeSecureSelectMasterFile(channel); + + // Select DF + byte[] apdu_2 = new byte[] { + + (byte)0x00, // CLA + (byte)0xA4, // INS + (byte)0x00, // P1 + (byte)0x00, // P2 + (byte)0x02, // Lc + path[0], + path[1] + }; + + executeSecureSelect(channel, apdu_2); + + // Select EF + byte[] apdu_3 = new byte[] { + + (byte)0x00, // CLA + (byte)0xA4, // INS + (byte)0x00, // P1 + (byte)0x00, // P2 + (byte)0x02, // Lc + path[2], + path[3] + }; + + byte[] fci = executeSecureSelect(channel, apdu_3); + + System.out.println("FCI of File:"); + printByteArray(fci); + + byte[] file = executeSecureReadBinary(channel, fci[7], fci[8]); + writeDataToFile(file, "f:/asn1.bin"); + return file; + + } + + private byte[] executeSecureSelect(CardChannel channel, byte[] apdu) throws CardException { + + System.out.print("Executing secure select command.."); + +// System.out.println("kEnc:"); +// printByteArray(this.kEnc); + +// this.kEnc[0] = (byte)(this.kEnc[0]+ (byte)0x01); + +// System.out.println("kEnc after modification:"); +// printByteArray(this.kEnc); + + +// byte[] apdu = new byte[] { +// +// // Select by name "Master.File" +// (byte) 0x00, (byte) 0xA4, (byte) 0x04, (byte) 0x00, (byte) 0x0B, +// (byte) 0x4D, (byte) 0x61, (byte) 0x73, (byte) 0x74, +// (byte) 0x65, (byte) 0x72, (byte) 0x2E, (byte) 0x46, +// (byte) 0x69, (byte) 0x6C, (byte) 0x65 + +// (byte) 0x00, (byte)0xA4, (byte)0x04, (byte)0x00, (byte)0x02, (byte)0x3F, (byte)0x00 + +// (byte)0x00, (byte)0xA4, (byte)0x04, (byte)0x00, (byte)0x0C, (byte)0xA0, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x63, (byte)0x50, (byte)0x4B, (byte)0x43, (byte)0x53, (byte)0x2D, (byte)0x31, (byte)0x35 + +// (byte)0x00, (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x05 + + // PIN VERIFY +// (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x00, (byte)0x08, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 + + // MSE +// (byte)0x00, (byte)0x22, (byte)0x41, (byte)0xB6, (byte)0x04, (byte)0x84, (byte)0x02, (byte)0x01, (byte)0x01 + + // SIGNATURE +// (byte)0x00, (byte)0x2A, (byte)0x9E, (byte)0x9A, (byte)0x23, (byte)0x30, (byte)0x21, (byte)0x30, (byte)0x09, +// (byte)0x06, (byte)0x05, (byte)0x2B, (byte)0x0E, (byte)0x03, (byte)0x02, (byte)0x1A, (byte)0x05, (byte)0x00, +// (byte)0x04, (byte)0x14, (byte)0x83, (byte)0x9B, (byte)0x54, (byte)0x3F, (byte)0xB0, (byte)0x9D, (byte)0x20, +// (byte)0xC8, (byte)0x03, (byte)0xB4, (byte)0x6E, (byte)0x6F, (byte)0x8F, (byte)0x07, (byte)0x47, (byte)0x24, +// (byte)0x49, (byte)0x51, (byte)0x82, (byte)0x2F +// }; + + + byte[] securedApdu = getSecuredAPDU(apdu); + +// System.out.println("Secured APDU:"); +// printByteArray(securedApdu); + + + CommandAPDU command = new CommandAPDU(securedApdu); +// System.out.println("Sending APDU: " + command.toString()); +// printByteArray(securedApdu); + ResponseAPDU resp = channel.transmit(command); + +// System.out.println("Response: " + Integer.toHexString(resp.getSW())); +// printByteArray(resp.getData()); + + byte[] data = executeGetResponse(channel, (byte)resp.getSW2()); + +// System.out.println("Encrypted response from card:"); +// printByteArray(data); + + byte[] response = verifyAndDecryptSecuredResponseAPDU(data); + +// System.out.println("Decrypted response from card:"); +// printByteArray(response); + + if(response.length >= 2 && response[response.length-2] == (byte)0x90 && response[response.length-1] == (byte)0x00) { + + System.out.println("OK"); + } else { + + System.out.println("FAILED"); + } + + byte[] fci = new byte[response.length-2]; + System.arraycopy(response, 0, fci, 0, response.length-2); + + return fci; + + } + + + private byte[] getRandomBytes(int length) { + + byte[] result = new byte[length]; + + for (int i = 0; i < length; i++) { + + Random rand = new Random(); + byte current = (byte) rand.nextInt(255); + result[i] = current; + } + + return result; + } + + private void calculateChannelKey() throws CardException { + +// this.kifd = new byte[] { +// +// (byte)0xcd, (byte)0x97, (byte)0xdb, (byte)0xf2, (byte)0x23, (byte)0x57, (byte)0x60, (byte)0xd8, (byte)0xb3, (byte)0xe9, (byte)0xc4, (byte)0xd7, (byte)0xe9, (byte)0xa6, (byte)0x91, (byte)0xfb +// , (byte)0x12, (byte)0xcd, (byte)0x20, (byte)0x7c, (byte)0x42, (byte)0x66, (byte)0x0d, (byte)0xf9, (byte)0xc6, (byte)0x93, (byte)0x57, (byte)0xba, (byte)0x7b, (byte)0x25, (byte)0xd0, (byte)0xfc +// }; +// +// this.kicc = new byte[] { +// +// (byte)0x88, (byte)0x6b, (byte)0x2b, (byte)0x01, (byte)0x5c, (byte)0xe1, (byte)0xc9, (byte)0x52, (byte)0x9d, (byte)0x94, (byte)0x63, (byte)0x69, (byte)0x5f, (byte)0xab, (byte)0xd1, (byte)0xa4 +// , (byte)0xa4, (byte)0xb8, (byte)0x5e, (byte)0x0a, (byte)0x1c, (byte)0xcd, (byte)0x4f, (byte)0x55, (byte)0x29, (byte)0x2c, (byte)0x20, (byte)0xe2, (byte)0x1e, (byte)0x95, (byte)0x4d, (byte)0x31 +// }; +// +// this.rndIcc = new byte[] { +// +// (byte)0xea, (byte)0xef, (byte)0xa8, (byte)0xfb, (byte)0xd3, (byte)0x1a, (byte)0xc8, (byte)0xec +// }; +// +// this.rndIfd = new byte[] { +// +// (byte)0x23, (byte)0x1D, (byte)0xac, (byte)0x73, (byte)0x7B, (byte)0xa0, (byte)0xfe, (byte)0x6e +// }; + + + System.out.print("Generating channel keys.."); + + if (this.kicc == null || this.kifd == null) { + + throw new CardException("Required key data not available."); + } + + if (this.kicc.length != this.kifd.length) { + + throw new CardException("Required key data is invalid."); + } + + byte[] kifdicc = new byte[this.kicc.length]; + + for (int i = 0; i < kifdicc.length; i++) { + + kifdicc[i] = (byte) (this.kicc[i] ^ this.kifd[i]); + } + + byte[] kEncHashData = new byte[kifdicc.length + + KENC_COMPUTATION_TAIL.length]; + byte[] kMacHashData = new byte[kifdicc.length + + KMAC_COMPUTATION_TAIL.length]; + + for (int i = 0; i < kifdicc.length; i++) { + + kEncHashData[i] = kifdicc[i]; + kMacHashData[i] = kifdicc[i]; + } + + for (int i = 0; i < KENC_COMPUTATION_TAIL.length; i++) { + + kEncHashData[i + kifdicc.length] = KENC_COMPUTATION_TAIL[i]; + } + + for (int i = 0; i < KMAC_COMPUTATION_TAIL.length; i++) { + + kMacHashData[i + kifdicc.length] = KMAC_COMPUTATION_TAIL[i]; + } + + byte[] hashEnc = computeHash(kEncHashData); + byte[] hashMac = computeHash(kMacHashData); + + this.kEnc = Arrays.copyOfRange(hashEnc, 0, 16); + this.kMac = Arrays.copyOfRange(hashMac, 0, 16); + + // printByteArray(kEnc); + // printByteArray(kMac); + + // compute sequence counter SSC + if (this.rndIcc == null || this.rndIfd == null + || this.rndIcc.length < 4 || this.rndIfd.length < 4) { + + throw new CardException("Data required to compute SSC not valid."); + } + + this.ssc = new byte[8]; + + for (int i = 0; i < 4; i++) { + + this.ssc[i] = this.rndIcc[this.rndIcc.length - 4 + i]; + } + + for (int i = 0; i < 4; i++) { + + this.ssc[i + 4] = this.rndIfd[this.rndIfd.length - 4 + i]; + } + + // printByteArray(ssc); + System.out.println("done."); + +// System.out.println("Kenc:"); +// printByteArray(this.kEnc); +// +// System.out.println("Kmac:"); +// printByteArray(this.kMac); +// +// System.out.println("SSC:"); +// printByteArray(this.ssc); + + } + + private byte[] verifyAndDecryptSecuredResponseAPDU(byte[] securedAPDU) throws CardException { + + // FOR TEST PURPOSES +// this.kEnc = new byte[] { +// +// (byte) 0x59, (byte) 0x8f, (byte) 0x26, (byte) 0xe3, (byte) 0x6e, +// (byte) 0x11, (byte) 0xa8, (byte) 0xec, (byte) 0x14, +// (byte) 0xb8, (byte) 0x1e, (byte) 0x19, (byte) 0xbd, +// (byte) 0xa2, (byte) 0x23, (byte) 0xca }; +// +// this.kMac = new byte[] { +// +// (byte) 0x5d, (byte) 0xe2, (byte) 0x93, (byte) 0x9a, (byte) 0x1e, +// (byte) 0xa0, (byte) 0x3a, (byte) 0x93, (byte) 0x0b, +// (byte) 0x88, (byte) 0x20, (byte) 0x6d, (byte) 0x8f, +// (byte) 0x73, (byte) 0xe8, (byte) 0xa7 }; +// +// this.ssc = new byte[] { +// +// (byte) 0xd3, (byte) 0x1a, (byte) 0xc8, (byte) 0xec, (byte) 0x7b, +// (byte) 0xa0, (byte) 0xfe, (byte) 0x75 }; +// +// securedAPDU = new byte[] { +// (byte)0x87, (byte) 0x21, (byte) 0x01, (byte) 0xbb, (byte) 0x10, (byte) 0xc8, (byte) 0x91, (byte) 0x5f, (byte) 0x0d, (byte) 0xdb, (byte) 0x63, (byte) 0x0c, (byte) 0xf3, (byte) 0x8e, (byte) 0xd7, (byte) 0x99, (byte) 0xa2, (byte) 0x02, (byte) 0x64, (byte) 0x7c, (byte) 0xa4, (byte) 0x74, (byte) 0x49, (byte) 0xd2 +// , (byte) 0x79, (byte) 0x13, (byte) 0x9f, (byte) 0xc2, (byte) 0x26, (byte) 0xd2, (byte) 0x5c, (byte) 0x20, (byte) 0x76, (byte) 0x56, (byte) 0x45, (byte) 0x99, (byte) 0x02, (byte) 0x90, (byte) 0x00, (byte) 0x8e, (byte) 0x04, (byte) 0x9a, (byte) 0xa7, (byte) 0x77, (byte) 0x07 +// }; + +// System.out.println("APDU to verify:"); +// printByteArray(securedAPDU); + + byte[] data = new byte[securedAPDU.length - 10]; + byte[] commandResponse = new byte[4]; + byte[] obtainedMac = new byte[4]; + + System.arraycopy(securedAPDU, 0, data, 0, data.length); + System.arraycopy(securedAPDU, data.length, commandResponse, 0, commandResponse.length); + System.arraycopy(securedAPDU, data.length+commandResponse.length+2, obtainedMac, 0, obtainedMac.length); + +// System.out.println("Extracted data:"); +// printByteArray(data); +// System.out.println("Extracted command response:"); +// printByteArray(commandResponse); +// System.out.println("Extracted MAC:"); +// printByteArray(obtainedMac); + + byte[] macData = new byte[data.length + commandResponse.length]; + System.arraycopy(data, 0, macData, 0, data.length); + System.arraycopy(commandResponse, 0, macData, data.length, commandResponse.length); + + byte[] paddedMacData = applyPadding(8, macData); + +// System.out.println("padded macData: "); +// printByteArray(paddedMacData); + + incrementSSC(); + + byte[] mac = calculateMAC(paddedMacData, this.kMac, this.ssc); + +// System.out.println("Computed MAC:"); +// printByteArray(mac); +// +// System.out.println("Obtained MAC:"); +// printByteArray(obtainedMac); + + if(!Arrays.equals(mac, obtainedMac)) { + + throw new CardException("Unable to verify MAC of response APDU."); + } else { + +// System.out.println("MAC OK!"); + } + + if(data.length > 0) { + + byte[] data2decrypt = new byte[data.length - getCutOffLength(data, 8)]; + System.arraycopy(data, getCutOffLength(data, 8), data2decrypt, 0, data2decrypt.length); + +// System.out.println("Data to be decrypted:"); +// printByteArray(data2decrypt); + + byte[] plainData = null; + + try { + plainData = perform3DESCipherOperation(data2decrypt, this.kEnc, Cipher.DECRYPT_MODE); + } catch (Exception e) { + throw new CardException("Unable to decrypt data.", e); + } + +// System.out.println("Plain data:"); +// printByteArray(plainData); + + byte[] unpaddedData = removePadding(plainData); + + byte[] result = new byte[unpaddedData.length + 2]; + System.arraycopy(unpaddedData, 0, result, 0, unpaddedData.length); + result[result.length-2] = commandResponse[2]; + result[result.length-1] = commandResponse[3]; + + return result; + } else { + + // no data in response + byte[] result = new byte[2]; + result[result.length-2] = commandResponse[2]; + result[result.length-1] = commandResponse[3]; + return result; + } + } + +// private int getLengthValue(byte[] data, int blockLen) throws CardException { +// +// +// int len = data.length; +// +// if( (len-3) % blockLen == 0) { +// +// // length of length tag is probably 1 -> check +// if(data[2] == (byte)0x01) { +// +// return 1; +// } +// } +// +// if( (len-4) % blockLen == 0) { +// +// // length of length tag is probably 2 -> check +// if(data[3] == (byte)0x01) { +// +// return 2; +// } +// +// } +// +// throw new CardException("Unable to determine length of length tag"); +// +// } + + private int getCutOffLength(byte[] data, int blockSize) throws CardException { + + int len = data.length % blockSize; + + // verify + if(data[len-1] == (byte)0x01) { + + return len; + } else { + throw new CardException("Unable to reconstruct encrypted datablock."); + } + + } + + private byte[] getSecuredAPDU(byte[] apdu) throws CardException { + + // FOR TEST PURPOSES +// this.kEnc = new byte[] { +// +// (byte) 0x59, (byte) 0x8f, (byte) 0x26, (byte) 0xe3, (byte) 0x6e, +// (byte) 0x11, (byte) 0xa8, (byte) 0xec, (byte) 0x14, +// (byte) 0xb8, (byte) 0x1e, (byte) 0x19, (byte) 0xbd, +// (byte) 0xa2, (byte) 0x23, (byte) 0xca }; +// +// this.kMac = new byte[] { +// +// (byte) 0x5d, (byte) 0xe2, (byte) 0x93, (byte) 0x9a, (byte) 0x1e, +// (byte) 0xa0, (byte) 0x3a, (byte) 0x93, (byte) 0x0b, +// (byte) 0x88, (byte) 0x20, (byte) 0x6d, (byte) 0x8f, +// (byte) 0x73, (byte) 0xe8, (byte) 0xa7 }; +// +// this.ssc = new byte[] { +// +// (byte) 0xd3, (byte) 0x1a, (byte) 0xc8, (byte) 0xec, (byte) 0x7b, +// (byte) 0xa0, (byte) 0xfe, (byte) 0x6e }; + + if (apdu == null || apdu.length < 4) { + + throw new CardException("Invalid APDU to secure."); + } + + if (apdu.length < 6) { + + // TODO: Handle cases: (a) CLA INS P1 P2, (b) CLA INS P1 P2 LE + + if(apdu.length == 5) { + + // handle case CLA INS P1 P2 LE + + byte encCLA = (byte)(apdu[0] | (byte)0x0C); + byte[] encHeader = new byte[]{encCLA, apdu[1], apdu[2], apdu[3]}; + byte[] paddedHeader = applyPadding(8, encHeader); + + byte[] leField = new byte[3]; + leField[0] = (byte)0x97; + leField[1] = (byte)0x01; + leField[2] = apdu[4]; + + byte[] macData = new byte[paddedHeader.length + leField.length]; + System.arraycopy(paddedHeader, 0, macData, 0, paddedHeader.length); + System.arraycopy(leField, 0, macData, paddedHeader.length, leField.length); + + byte[] paddedMacData = applyPadding(8, macData); + + incrementSSC(); + +// System.out.println("Calculating MAC based on following data:"); +// printByteArray(paddedMacData); + + byte[] mac = calculateMAC(paddedMacData, kMac, this.ssc); + + byte[] encapsulatedMac = new byte[mac.length + 2]; + encapsulatedMac[0] = (byte)0x8E; + encapsulatedMac[1] = (byte)mac.length; + System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length); + + byte[] completeMessage = new byte[5 + leField.length + encapsulatedMac.length]; + completeMessage[0] = encCLA; + completeMessage[1] = apdu[1]; + completeMessage[2] = apdu[2]; + completeMessage[3] = apdu[3]; + completeMessage[4] = (byte)(encapsulatedMac.length + leField.length); + System.arraycopy(leField, 0, completeMessage, 5, leField.length); + System.arraycopy(encapsulatedMac, 0, completeMessage, 5+leField.length, encapsulatedMac.length); + + return completeMessage; + } + + + + return null; + } + + // case data field available + + byte cla = apdu[0]; + byte ins = apdu[1]; + byte p1 = apdu[2]; + byte p2 = apdu[3]; + byte lc = apdu[4]; + + byte[] data = new byte[lc]; + System.arraycopy(apdu, 5, data, 0, lc); + + byte[] paddedData = applyPadding(8, data); + + byte[] encrypted = null; + + try { + + encrypted = perform3DESCipherOperation(paddedData, kEnc, Cipher.ENCRYPT_MODE); +// printByteArray(encrypted); + + } catch (Exception e) { + + throw new CardException("Error encrypting APDU.", e); + } + + byte[] encapsulated = new byte[encrypted.length + 3]; + encapsulated[0] = (byte)0x87; + encapsulated[1] = (byte)(encrypted.length + 1); + encapsulated[2] = (byte)0x01; + System.arraycopy(encrypted, 0, encapsulated, 3, encrypted.length); + +// printByteArray(encapsulated); + + + // calculate MAC + + // prepare CLA byte + + byte encCLA = (byte)(cla | (byte)0x0C); + byte[] encHeader = new byte[]{encCLA, ins, p1, p2}; + byte[] paddedHeader = applyPadding(8, encHeader); + + byte[] headerAndData = new byte[paddedHeader.length + encapsulated.length]; + System.arraycopy(paddedHeader, 0, headerAndData, 0, paddedHeader.length); + System.arraycopy(encapsulated, 0, headerAndData, paddedHeader.length, encapsulated.length); + + byte[] paddedHeaderAndData = applyPadding(8, headerAndData); + +// printByteArray(paddedHeaderAndData); + + incrementSSC(); +// printByteArray(this.ssc); + + byte[] mac = calculateMAC(paddedHeaderAndData, kMac, this.ssc); +// printByteArray(mac); + + byte[] encapsulatedMac = new byte[mac.length + 2]; + encapsulatedMac[0] = (byte)0x8E; + encapsulatedMac[1] = (byte)mac.length; + System.arraycopy(mac, 0, encapsulatedMac, 2, mac.length); + +// printByteArray(encapsulatedMac); + + byte[] completeMessage = new byte[5 + encapsulated.length + encapsulatedMac.length]; + completeMessage[0] = encCLA; + completeMessage[1] = ins; + completeMessage[2] = p1; + completeMessage[3] = p2; + completeMessage[4] = (byte)(encapsulated.length + encapsulatedMac.length); + System.arraycopy(encapsulated, 0, completeMessage, 5, encapsulated.length); + System.arraycopy(encapsulatedMac, 0, completeMessage, 5+encapsulated.length, encapsulatedMac.length); + +// printByteArray(completeMessage); + + return completeMessage; + } + + private void incrementSSC() { + + BigInteger ssc = new BigInteger(this.ssc); + ssc = ssc.add(new BigInteger("1", 10)); + this.ssc = ssc.toByteArray(); + } + + + private byte[] xorByteArrays(byte[] array1, byte[] array2) throws CardException { + + if(array1 == null || array2 == null || array1.length != array2.length) { + + throw new CardException("Cannot xor byte arrays - invalid input."); + } + + byte[] result = new byte[array1.length]; + + for(int i=0; i= 0; i--) { + + byte current = paddedData[i]; + + if(current == (byte)0x00) { + + continue; + } + + if(current == (byte)0x80) { + + // end of padding reached + byte[] data = new byte[i]; + System.arraycopy(paddedData, 0, data, 0, i); + return data; + + } else { + + throw new CardException("Wrong padding."); + } + + } + + //should never happen + return null; + + } + + private byte[] applyPadding(int blockSize, byte[] data) { + + // add mandatory 0x80 + byte[] extended = new byte[data.length + 1]; + System.arraycopy(data, 0, extended, 0, data.length); + extended[extended.length - 1] = (byte) 0x80; + + if (extended.length % blockSize == 0) { + + return extended; + } + + int requiredBlocks = ((int) (extended.length / blockSize) + 1); + + byte[] result = new byte[requiredBlocks * blockSize]; + Arrays.fill(result, (byte) 0x00); + System.arraycopy(extended, 0, result, 0, extended.length); + + return result; + + } + + private byte[] executeGetChipInfo(CardChannel channel) throws CardException { + + // get chip info - read out card serial number + System.out.print("Getting chip info.."); + CommandAPDU command = new CommandAPDU(0x90, 0xB8, 0x00, 0x00, 0x07); + ResponseAPDU resp = channel.transmit(command); + + if (resp.getSW() == 0x9000) { + System.out.println("done."); + } else { + System.out.println("error: " + Integer.toHexString(resp.getSW())); + } + + System.out.println("Read chip info:"); + printByteArray(resp.getData()); + + return resp.getData(); + } + + private byte[] executeReadCertificate(CardChannel channel, byte[] id) + throws CardException { + + // certificate + System.out.print("Reading certificate.."); + + byte[] apdu = new byte[] { (byte) 0x00, (byte) 0xA4, (byte) 0x00, + (byte) 0x00, (byte) 0x02, id[0], id[1] }; + CommandAPDU command = new CommandAPDU(apdu); + ResponseAPDU resp = channel.transmit(command); + + byte certLenHigh = 0x00; + byte certLenLow = 0x00; + + if (resp.getSW() == 0x9000) { + // Selection successful - FCI is already in response + byte[] fci = resp.getData(); + certLenHigh = fci[7]; + certLenLow = fci[8]; + + } else if (resp.getSW1() == 0x61) { + // Selection successful - FCI ready to be read +// int fciLen = resp.getSW2(); +// command = new CommandAPDU(new byte[] { (byte) 0x00, (byte) 0xC0, +// (byte) 0x00, (byte) 0x00, (byte) fciLen }); +// resp = channel.transmit(command); +// +// byte[] fci = resp.getData(); + byte[] fci = executeGetResponse(channel, (byte)resp.getSW2()); + + certLenHigh = fci[7]; + certLenLow = fci[8]; + + } else { + + throw new CardException("Unexpected response from card: " + + Integer.toHexString(resp.getSW())); + } + + ByteArrayOutputStream bof = new ByteArrayOutputStream(); + + int bytes2read = (certLenHigh * 256) + certLenLow; + int bytesRead = 0; + + boolean done = false; + int offset = 0; + int len = 0; + + while (!done) { + + if (bytes2read - bytesRead > 255) { + len = 255; + } else { + len = bytes2read - bytesRead; + } + + byte[] offsetBytes = intToHex(offset); + + command = new CommandAPDU(0x00, 0xB0, offsetBytes[0], + offsetBytes[1], len); + resp = channel.transmit(command); + + byte[] certData = resp.getData(); + + try { + bof.write(certData); + } catch (IOException e) { + throw new CardException("Error reading certificate from card", + e); + } + + bytesRead = bytesRead + certData.length; + offset = bytesRead; + + if (bytesRead == bytes2read) { + + done = true; + } + } + + System.out.println("done."); + + return bof.toByteArray(); + } + + private void verifyCertificates() throws CardException { + + RSAPublicKey rootPubKey = createRSAPublicKey(ROOT_CA_MODULO, ROOT_CA_PUBEXP); + + X509Certificate intermediate = createCertificate(intermediateCert); + X509Certificate component = createCertificate(componentCert); + + try { + component.verify(intermediate.getPublicKey()); + intermediate.verify(rootPubKey); + } catch (Exception e) { + + System.out.println("Certificate verification failed."); + e.printStackTrace(); + } + } + + + public void establishSecureChannel() throws CardException { + + CardChannel channel = setupCardChannel(); + + // get card serial number + this.snIcc = executeGetChipInfo(channel); + System.out.println("Card Serial Number"); + printByteArray(snIcc); + + // get card certificates + this.intermediateCert = executeReadCertificate(channel, new byte[] { + (byte) 0x60, (byte) 0x20 }); + printByteArray(this.intermediateCert); + writeDataToFile(intermediateCert, "f:/secure_channel_card_intermediate.cer"); + this.componentCert = executeReadCertificate(channel, new byte[] { + (byte) 0x60, (byte) 0x1F }); + printByteArray(this.componentCert); + writeDataToFile(componentCert, "f:/secure_channel_card_component.cer"); + + // TODO: Verify certificate chain + verifyCertificates(); + + + // load certs and select keys + loadCertsAndSelectKeys(channel); + + // internal authentication + internalAuthentication(channel); + + // external authentication + externalAuthentication(channel); + + // derive channel key + calculateChannelKey(); + + // test secure channel +// executeSecureSelect(channel); +// executeSecureSelect(channel); +// executeSecureSelect(channel); +// executeSecureSelect(channel); +// executeSecureSelect(channel); + + //VERIFY PIN + executeSecurePINVerify(channel); + + // GET PrKDF + executeSecureReadPrKDF(channel); + + // Manage Security Environment + executeSecureManageSecurityEnvironment(channel); + + // Create signature + executeSecurePerformSecurityOperation(channel); + + // GET CDF + executeSecureReadCDF(channel); + + // Select certificate + executeSecureSelectCertificate(channel); + + // Verify signature + verifySignature(); + + } + + private void internalAuthentication(CardChannel channel) + throws CardException { + + byte[] randomBytes = getRandomBytes(8); + byte[] challengeData = new byte[randomBytes.length + RANDOM_TAIL.length]; + + this.rndIfd = randomBytes; + + for (int i = 0; i < randomBytes.length; i++) { + + challengeData[i] = randomBytes[i]; + } + for (int i = 0; i < RANDOM_TAIL.length; i++) { + + challengeData[i + randomBytes.length] = RANDOM_TAIL[i]; + } + +// System.out.println("Generated terminal challenge:"); +// printByteArray(challengeData); + + // send challenge to card + CommandAPDU command_6 = new CommandAPDU((byte) 0x00, (byte) 0x88, + (byte) 0x00, (byte) 0x00, challengeData); + ResponseAPDU resp_6 = channel.transmit(command_6); + +// System.out.println("Challenge:"); +// printByteArray(challengeData); + + System.out.println("Response to terminal challenge: " + + Integer.toHexString(resp_6.getSW())); + + byte[] data = null; + + if (resp_6.getSW() == 0x9000) { + + data = resp_6.getData(); + + } else if (resp_6.getSW1() == 0x61) { + + data = executeGetResponse(channel, (byte)resp_6.getSW2()); + + } else { + + System.out.println("An error occured - cancel."); + throw new CardException("Internal authentication failed - " + + Integer.toHexString(resp_6.getSW())); + } + + // verify response + boolean ok = verifyInternalAuthenticationResponse(randomBytes, data); + + System.out.println("Internal Authentiction succeeded: " + ok); + + if (!ok) { + + System.out.println("Internal Authentiction failed - cancel."); + throw new CardException("Internal authentication failed"); + } + + } + + private void loadCertsAndSelectKeys(CardChannel channel) + throws CardException { + + CommandAPDU command_1 = new CommandAPDU((byte) 0x00, (byte) 0x22, + (byte) 0x81, (byte) 0xB6, new byte[] { (byte) 0x83, + (byte) 0x02, (byte) 0x02, (byte) 0x0F }); + ResponseAPDU resp_1 = channel.transmit(command_1); + + System.out.println("Response: " + Integer.toHexString(resp_1.getSW())); + + CommandAPDU command_2 = new CommandAPDU((byte) 0x00, (byte) 0x2A, + (byte) 0x00, (byte) 0xAE, C_CV_CA); + ResponseAPDU resp_2 = channel.transmit(command_2); + + System.out.println("Response: " + Integer.toHexString(resp_2.getSW())); + + CommandAPDU command_3 = new CommandAPDU((byte) 0x00, (byte) 0x22, + (byte) 0x81, (byte) 0xB6, CHR); + ResponseAPDU resp_3 = channel.transmit(command_3); + + System.out.println("Response: " + Integer.toHexString(resp_3.getSW())); + + CommandAPDU command_4 = new CommandAPDU((byte) 0x00, (byte) 0x2A, + (byte) 0x00, (byte) 0xAE, C_CV_IFD); + ResponseAPDU resp_4 = channel.transmit(command_4); + + System.out.println("Response: " + Integer.toHexString(resp_4.getSW())); + + CommandAPDU command_5 = new CommandAPDU((byte) 0x00, (byte) 0x22, + (byte) 0xC1, (byte) 0xA4, KEY_SELECTOR); + ResponseAPDU resp_5 = channel.transmit(command_5); + + System.out.println("Response: " + Integer.toHexString(resp_5.getSW())); + + } + + private byte[] computeHash(byte[] input) throws CardException { + + byte[] digest = null; + + try { + MessageDigest sha = MessageDigest.getInstance("SHA"); + + sha.update(input); + digest = sha.digest(); + + } catch (NoSuchAlgorithmException e) { + throw new CardException("Error computing hash.", e); + } + + return digest; + } + + private void externalAuthentication(CardChannel channel) + throws CardException { + + System.out.println("Starting external authentication.."); + + // request card challenge +// System.out.print("Requesting card challenge.."); + CommandAPDU command_7 = new CommandAPDU((byte) 0x00, (byte) 0x84, + (byte) 0x00, (byte) 0x00, (byte) 0x08); + ResponseAPDU resp_7 = channel.transmit(command_7); + byte[] cardChallenge = resp_7.getData(); + +// System.out.println("Obtained card challenge:"); +// printByteArray(cardChallenge); + +// System.out.println(Integer.toHexString(resp_7.getSW())); + + this.rndIcc = cardChallenge; + + // System.out.println("Card Challenge:"); + // printByteArray(cardChallenge); + + byte[] prnd2 = getRandomBytes(this.prndLength); + byte[] kIfd = getRandomBytes(32); + +// System.out.println("Created KIFD:"); +// printByteArray(kIfd); + + // byte[] prnd2 = TEST_PRND2; + // byte[] kIfd = TEST_KIFD; + // byte[] cardChallenge = TEST_CARD_CHALLENGE; + // snIcc = new + // byte[]{(byte)0x06,(byte)0x5A,(byte)0x85,(byte)0xCA,(byte)0x58,(byte)0x6F,(byte)0x32}; + + // System.out.println("Card Serial Number:"); + // printByteArray(snIcc); + + // compute hash + byte[] hashData = new byte[prnd2.length + kIfd.length + + cardChallenge.length + 8]; + + for (int i = 0; i < prnd2.length; i++) { + + hashData[i] = prnd2[i]; + } + + for (int i = 0; i < kIfd.length; i++) { + + hashData[prnd2.length + i] = kIfd[i]; + } + + for (int i = 0; i < cardChallenge.length; i++) { + + hashData[prnd2.length + kIfd.length + i] = cardChallenge[i]; + } + + int snPadding = 8 - snIcc.length; + + for (int i = 0; i < snPadding; i++) { + + hashData[prnd2.length + kIfd.length + cardChallenge.length + i] = (byte) 0x00; + } + + for (int i = 0; i < snIcc.length; i++) { + + hashData[prnd2.length + kIfd.length + cardChallenge.length + + snPadding + i] = snIcc[i]; + } + + // System.out.println("HashData:"); + // printByteArray(hashData); + + byte[] digest = null; + + try { + MessageDigest sha = MessageDigest.getInstance("SHA"); + + sha.update(hashData); + digest = sha.digest(); + + } catch (NoSuchAlgorithmException e) { + throw new CardException("Error computing hash.", e); + } + + // System.out.println("Hash:"); + // printByteArray(digest); + + byte[] plain = new byte[2 + prnd2.length + kIfd.length + digest.length]; + + plain[0] = (byte) 0x6A; + + for (int i = 0; i < prnd2.length; i++) { + + plain[i + 1] = prnd2[i]; + } + + for (int i = 0; i < kIfd.length; i++) { + + plain[i + 1 + prnd2.length] = kIfd[i]; + } + + for (int i = 0; i < digest.length; i++) { + + plain[i + 1 + prnd2.length + kIfd.length] = digest[i]; + } + + plain[plain.length - 1] = (byte) 0xBC; + + // encrypt plain data + RSAPrivateKey terminalPrivateKey = createRSAPrivateKey(TERMINAL_MODULO, + TERMINAL_PRIVEXP); + + byte[] encResult = null; + try { + encResult = rsaEncrypt(terminalPrivateKey, plain); + } catch (Exception e) { + + throw new CardException("Error encrypting authentication data.", e); + } + + // System.out.println("SIG:"); + // printByteArray(encResult); + + // apply MIN function + BigInteger sig = new BigInteger(encResult); + BigInteger mod = new BigInteger(TERMINAL_MODULO, 16); + + BigInteger diff = mod.subtract(sig); + + BigInteger sigMin = diff.min(sig); + + // System.out.println("SIGMIN:"); + // printByteArray(sigMin.toByteArray()); + + PublicKey cardPubKey = null; + // encrypt with card public key +// try { +// X509Certificate cert = new X509Certificate(componentCert); + X509Certificate cert = createCertificate(componentCert); + + cardPubKey = cert.getPublicKey(); +// } catch (CertificateException e) { +// +// throw new CardException( +// "Error retrieving public key from certificate.", e); +// } + + byte[] authData = null; + try { + authData = rsaEncrypt(cardPubKey, sigMin.toByteArray()); + } catch (Exception e) { + + throw new CardException("Error encrypting authentication data."); + } + + // System.out.println("Authentication data:"); + // printByteArray(authData); + + // send auth data to card + // BE CAREFUL WITH THAT!!! EXT-AUTH METHOD MAY GET BLOCKED!!! + + System.out.print("Sending authentication data to card.."); + CommandAPDU command_8 = new CommandAPDU((byte) 0x00, (byte) 0x82, + (byte) 0x00, (byte) 0x00, authData); + ResponseAPDU resp_8 = channel.transmit(command_8); + + System.out.println(Integer.toHexString(resp_8.getSW())); + + if (resp_8.getSW() == 0x9000) { + + this.kifd = kIfd; + } + + } + + private boolean verifyInternalAuthenticationResponse( + byte[] terminalChallenge, byte[] resp) throws CardException { + + System.out.println("Verifying card response.."); + + // byte[] challenge = TEST_CHALLENGE; + // byte[] response = TEST_RESPONSE; + + byte[] challenge = terminalChallenge; + byte[] response = resp; + + // System.out.println("Cahllenge:"); + // printByteArray(challenge); + // + // System.out.println("Response:"); + // printByteArray(response); + + // decrypt response with terminal private key + byte[] plain = null; + RSAPrivateKey terminalPrivateKey = createRSAPrivateKey(TERMINAL_MODULO, + TERMINAL_PRIVEXP); + try { + plain = rsaDecrypt(terminalPrivateKey, response); + } catch (Exception e) { + throw new CardException("Error decrypting card response.", e); + } + + // decrypt intermediate result with card's public key + // CardChannel channel = setupCardChannel(); + // executeGetChipInfo(channel); + + // byte[] componentCert = executeReadCertificate(channel, new + // byte[]{(byte)0x60, (byte)0x1F}); + + PublicKey pubKey = null; + +// try { +// X509Certificate cert = new X509Certificate(componentCert); + X509Certificate cert = createCertificate(componentCert); + + pubKey = cert.getPublicKey(); + +// } catch (CertificateException e) { +// +// throw new CardException( +// "Error retrieving public key from certificate.", e); +// } + + byte[] sig = null; + + try { + sig = rsaDecrypt(pubKey, plain); + + } catch (Exception e) { + + throw new CardException("Error decrypting with card's public key", + e); + } + + if (sig == null) { + + throw new CardException("Computed value is null."); + } else { + + // System.out.println("decrypted response (SIG):"); + // printByteArray(sig); + + if (sig[0] == (byte) 0x6A && sig[sig.length - 1] == (byte) 0xBC) { + + // Obtained response from card was obviously SIG - nothing else + // to do here + + } else { + + // Obtained response from card was obviously N.ICC-SIG - + // compute N.ICC-SIG and decrypt result again + + RSAPublicKey rsaPubKey = (RSAPublicKey) pubKey; + BigInteger mod = rsaPubKey.getModulus(); + BigInteger sigVal = new BigInteger(plain); + +// System.out.println("MODULUS: " + mod); +// System.out.println("SIGVAL: " + sigVal); +// + BigInteger substractionResult = mod.subtract(sigVal); +// System.out.println("DIFFERENCE: " + substractionResult); + + + byte[] encrypted = substractionResult.toByteArray(); + +// System.out.println("data to be decrypted:"); +// printByteArray(encrypted); + + // necessary as substraction result seems to contain one leading zero byte + byte[] trimmed = new byte[128]; + System.arraycopy(encrypted, encrypted.length-128, trimmed, 0, 128); + + try { + sig = rsaDecrypt(pubKey, trimmed); + + } catch (Exception e) { + + throw new CardException("Error decrypting response.", e); + } + } + } + + // extract data from decrypted response + byte[] hash = new byte[20]; + byte[] kIcc = new byte[32]; + byte[] prnd1 = new byte[sig.length - 2 - 20 - 32]; + + this.prndLength = prnd1.length; + + for (int i = 0; i < prnd1.length; i++) { + + prnd1[i] = sig[i + 1]; // 1 byte offset due to 6A padding + } + + for (int i = 0; i < kIcc.length; i++) { + + kIcc[i] = sig[i + 1 + prnd1.length]; + } + +// System.out.println("Got KICC from card:"); +// printByteArray(kIcc); + + for (int i = 0; i < hash.length; i++) { + + hash[i] = sig[i + 1 + prnd1.length + kIcc.length]; + } + + // verify hash + byte[] hashData = new byte[prnd1.length + kIcc.length + + challenge.length + 8]; + + for (int i = 0; i < prnd1.length; i++) { + + hashData[i] = prnd1[i]; + } + + for (int i = 0; i < kIcc.length; i++) { + + hashData[i + prnd1.length] = kIcc[i]; + } + + for (int i = 0; i < challenge.length; i++) { + + hashData[i + prnd1.length + kIcc.length] = challenge[i]; + } + + for (int i = 0; i < 8; i++) { + + hashData[i + prnd1.length + kIcc.length + challenge.length] = RANDOM_TAIL[i]; + } + + byte[] digest = null; + + try { + MessageDigest sha = MessageDigest.getInstance("SHA"); + + sha.update(hashData); + digest = sha.digest(); + + } catch (NoSuchAlgorithmException e) { + throw new CardException("Error computing hash.", e); + } + + boolean internalAuthResult = Arrays.equals(hash, digest); + + if (internalAuthResult) { + + // if verification succeeded, remember kicc + this.kicc = kIcc; + } + + return internalAuthResult; + + } + + // for test purposes + private void writeDataToFile(byte[] data, String filename) { + + FileOutputStream fos; + try { + fos = new FileOutputStream(filename); + fos.write(data); + fos.close(); + } catch (FileNotFoundException e) { + System.out.println("Error writing File: " + e.getMessage()); + e.printStackTrace(); + } catch (IOException e) { + System.out.println("Error writing File: " + e.getMessage()); + e.printStackTrace(); + } + + } + +// public static RSAPublicKey createRSAPublicKey(String mod, String pubExponent) { +// +// BigInteger modulo = new BigInteger(mod, 16); +// BigInteger pubExp = new BigInteger(pubExponent, 16); +// +// RSAPublicKey rsaPublicKey = new RSAPublicKey(modulo, pubExp); +// +// return rsaPublicKey; +// } + + public static RSAPublicKey createRSAPublicKey(String mod, String pubExponent) throws CardException { + + BigInteger modulus = new BigInteger(mod, 16); + BigInteger pubExp = new BigInteger(pubExponent, 16); + + KeyFactory fac; + RSAPublicKey key; + try { + fac = KeyFactory.getInstance("RSA"); + KeySpec spec = new RSAPublicKeySpec(modulus, pubExp); + key = (RSAPublicKey)fac.generatePublic(spec); + } catch (Exception e) { + + throw new CardException("Unable to create public key." , e); + } + + + return key; + } + + public static RSAPrivateKey createRSAPrivateKey(String mod, + String privExponent) throws CardException { + + BigInteger modulus = new BigInteger(mod, 16); + BigInteger privExp = new BigInteger(privExponent, 16); + + KeyFactory fac; + RSAPrivateKey key; + try { + fac = KeyFactory.getInstance("RSA"); + KeySpec spec = new RSAPrivateKeySpec(modulus, privExp); + key = (RSAPrivateKey)fac.generatePrivate(spec); + } catch (Exception e) { + + throw new CardException("Unable to create private key." , e); + } + + return key; + } + + + public static byte[] rsaEncrypt(Key key, byte[] data) + throws NoSuchAlgorithmException, NoSuchPaddingException, + InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + + Cipher rsa = Cipher.getInstance("RSA/ECB/NoPadding"); + rsa.init(Cipher.ENCRYPT_MODE, key); + byte[] encrypted = rsa.doFinal(data); + + return encrypted; + + } + + public static byte[] rsaDecrypt(Key key, byte[] cipher) + throws NoSuchAlgorithmException, NoSuchPaddingException, + InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + + Cipher rsa = Cipher.getInstance("RSA/ECB/NoPadding"); + rsa.init(Cipher.DECRYPT_MODE, key); + byte[] decrypted = rsa.doFinal(cipher); + + return decrypted; + } + + @Override + public byte[] createSignature(InputStream input, KeyboxName keyboxName, + PINGUI pinGUI, String alg) throws SignatureCardException, + InterruptedException, IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public byte[] getCertificate(KeyboxName keyboxName, PINGUI provider) + throws SignatureCardException, InterruptedException { + // TODO Auto-generated method stub + return null; + } + + @Override + public byte[] getInfobox(String infobox, PINGUI pinGUI, String domainId) + throws SignatureCardException, InterruptedException { + // TODO Auto-generated method stub + return null; + } + + private CardChannel setupCardChannel() throws CardException { + + // show the list of available terminals + TerminalFactory factory = TerminalFactory.getDefault(); + List terminals = factory.terminals().list(); + System.out.println("Terminals: " + terminals); + // get the first terminal + CardTerminal terminal = terminals.get(0); + // establish a connection with the card + Card card = terminal.connect("*"); + System.out.println("card: " + card); + CardChannel channel = card.getBasicChannel(); + + + return channel; + } + + private void printByteArray(byte[] data) { + + for (int i = 0; i < data.length; i++) { + + String s = Integer.toHexString(data[i]); + + if (s.length() == 1) { + s = "0" + s; + } + + if (s.length() > 2) { + s = s.substring(s.length() - 2); + } + + System.out.print(s + " "); + } + + System.out.println(); + } + + private static byte[] intToHex(int val) { + + String hexString = Integer.toHexString(val); + + if (hexString.length() > 4) { + System.out.println("Unexpected result. Quit."); + return null; + } + + byte high = 0x00; + byte low = 0x00; + + if (hexString.length() <= 2) { + + low = (byte) Integer.parseInt(hexString, 16); + } else { + + low = (byte) Integer.parseInt(hexString.substring(hexString + .length() - 2), 16); + high = (byte) Integer.parseInt(hexString.substring(0, hexString + .length() - 2), 16); + } + + return new byte[] { high, low }; + } + + public byte[] getComponentCert() { + return componentCert; + } + + public void setComponentCert(byte[] componentCert) { + this.componentCert = componentCert; + } + + public byte[] getIntermediateCert() { + return intermediateCert; + } + + public void setIntermediateCert(byte[] intermediateCert) { + this.intermediateCert = intermediateCert; + } + +} -- cgit v1.2.3