Java tutorial
/** * Copyright (C) 2014-2015 Martin Paljak * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package esteidhacker; import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAKeyGenParameterSpec; import java.util.Arrays; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.smartcardio.Card; import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; import javax.smartcardio.ResponseAPDU; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.crypto.RuntimeCryptoException; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.PEMKeyPair; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import esteidhacker.EstEID.CardType; public class FakeEstEID { // Other fun constants private static final String[] defaultDataFile = new String[] { "JNES-KARVANE", "SIILIPOISS", "Jess MARIA", "G", "LOL", "01.01.0001", "10101010005", "A0000001", "31.12.2099", "TIIBET", "01.01.2014", "ALALINE", "SEE POLE PRIS KAART", " ", " ", " " }; public static final byte[] aid = new byte[] { (byte) 0xD2, (byte) 0x33, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x45, (byte) 0x73, (byte) 0x74, (byte) 0x45, (byte) 0x49, (byte) 0x44, (byte) 0x20, (byte) 0x76, (byte) 0x33, (byte) 0x35 }; private final Card card; private final CardChannel channel; private FakeEstEID(Card card) { this.card = card; this.channel = card.getBasicChannel(); } public static FakeEstEID getInstance(EstEID esteid) { if (esteid.getType() == CardType.AnyJavaCard || esteid.getType() == CardType.JavaCard2011) { FakeEstEID fake = new FakeEstEID(esteid.getCard()); return fake; } return null; } public void send_cert(byte[] cert, int num) throws Exception { int chunksize = 240; // was:253 //card.beginExclusive(); try { byte[] c = org.bouncycastle.util.Arrays.append(cert, (byte) 0x80); for (int i = 0; i <= (c.length / chunksize); i++) { byte[] d = new byte[2 + chunksize]; int off = i * chunksize; d[0] = (byte) ((off & 0xFF00) >>> 8); d[1] = (byte) (off & 0xFF); byte[] chunk = Arrays.copyOfRange(c, i * chunksize, i * chunksize + chunksize); System.arraycopy(chunk, 0, d, 2, chunk.length); CommandAPDU cmd = new CommandAPDU(0x80, 0x02, num, 0x00, d); check(channel.transmit(cmd)); } } finally { //card.endExclusive(); } } public void send_cert_pem(File f, int num) throws Exception { PEMParser pem = new PEMParser(new InputStreamReader(new FileInputStream(f))); X509CertificateHolder crt = (X509CertificateHolder) pem.readObject(); pem.close(); send_cert(crt.getEncoded(), num); } public void send_new_key(int num) throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); keyGen.initialize(2048); //keyGen.initialize(new RSAKeyGenParameterSpec(2048, BigInteger.ONE)); KeyPair key = keyGen.generateKeyPair(); send_key((RSAPrivateCrtKey) key.getPrivate(), num); } public void send_key_pem(File f, int num) throws Exception { PEMParser pem = new PEMParser(new InputStreamReader(new FileInputStream(f))); // OpenSSL genrsa makes a key pair. Object o = pem.readObject(); RSAPrivateCrtKey key; if (o instanceof org.bouncycastle.openssl.PEMKeyPair) { PEMKeyPair pair = (PEMKeyPair) o; JcaPEMKeyConverter convert = new JcaPEMKeyConverter(); key = (RSAPrivateCrtKey) convert.getPrivateKey(pair.getPrivateKeyInfo()); } else { key = (RSAPrivateCrtKey) pem.readObject(); } pem.close(); send_key(key, num); } public void send_key(RSAPrivateCrtKey key, int num) throws CardException { //card.beginExclusive(); try { CommandAPDU cmd = null; cmd = new CommandAPDU(0x80, 0x03, num, 0x01, unsigned(key.getPrimeP())); check(channel.transmit(cmd)); cmd = new CommandAPDU(0x80, 0x03, num, 0x02, unsigned(key.getPrimeQ())); check(channel.transmit(cmd)); cmd = new CommandAPDU(0x80, 0x03, num, 0x03, unsigned(key.getPrimeExponentP())); check(channel.transmit(cmd)); cmd = new CommandAPDU(0x80, 0x03, num, 0x04, unsigned(key.getPrimeExponentQ())); check(channel.transmit(cmd)); cmd = new CommandAPDU(0x80, 0x03, num, 0x05, unsigned(key.getCrtCoefficient())); check(channel.transmit(cmd)); } finally { //card.endExclusive(); } } public void make_sample_card(FakeEstEIDCA ca, boolean check) throws Exception { KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", BouncyCastleProvider.PROVIDER_NAME); keyGen.initialize(2048); // Generate keys KeyPair auth = keyGen.generateKeyPair(); KeyPair sign = keyGen.generateKeyPair(); X509Certificate authcert = ca.generateUserCertificate((RSAPublicKey) auth.getPublic(), false, "SIILIPOISS", "UDUS", "10101010005", "kalevipoeg@soome.fi"); X509Certificate signcert = ca.generateUserCertificate((RSAPublicKey) sign.getPublic(), true, "SIILIPOISS", "UDUS", "10101010005", "kalevipoeg@soome.fi"); if (check) { // Verify softkeys if (!verifyKeypairIntegrity((RSAPrivateCrtKey) auth.getPrivate(), (RSAPublicKey) authcert.getPublicKey())) { throw new RuntimeCryptoException("Cert and key mismatch"); } if (!verifyKeypairIntegrity((RSAPrivateCrtKey) sign.getPrivate(), (RSAPublicKey) signcert.getPublicKey())) { throw new RuntimeCryptoException("Cert and key mismatch"); } } send_key((RSAPrivateCrtKey) auth.getPrivate(), 1); send_key((RSAPrivateCrtKey) sign.getPrivate(), 2); send_cert(authcert.getEncoded(), 1); send_cert(signcert.getEncoded(), 2); CommandAPDU cmd = null; ResponseAPDU resp = null; if (check) { // Verify on-card keys. Cipher verify_cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); SecureRandom r = SecureRandom.getInstance("SHA1PRNG"); byte[] rnd = new byte[8]; r.nextBytes(rnd); cmd = new CommandAPDU(0x00, 0x88, 0x00, 0x00, rnd, 256); resp = channel.transmit(cmd); check(resp); verify_cipher.init(Cipher.DECRYPT_MODE, authcert.getPublicKey()); byte[] result = verify_cipher.doFinal(resp.getData()); if (!java.util.Arrays.equals(rnd, result)) { throw new RuntimeCryptoException("Card and auth key don't match!"); } r.nextBytes(rnd); cmd = new CommandAPDU(0x00, 0x2A, 0x9E, 0x9A, rnd, 256); resp = channel.transmit(cmd); check(resp); verify_cipher.init(Cipher.DECRYPT_MODE, signcert.getPublicKey()); result = verify_cipher.doFinal(resp.getData()); if (!java.util.Arrays.equals(rnd, result)) { throw new RuntimeCryptoException("Card and sign key don't match!"); } } // Dump default data file for (int i = 0; i < defaultDataFile.length; i++) { cmd = new CommandAPDU(0x80, 0x04, i + 1, 0x00, defaultDataFile[i].toUpperCase().getBytes("ISO8859-15")); resp = channel.transmit(cmd); check(resp); } } private static byte[] unsigned(BigInteger num) { byte[] bytes = num.toByteArray(); if (bytes.length % 8 == 0) { return bytes; } else if (bytes[0] == 0x00) return Arrays.copyOfRange(bytes, 1, bytes.length); return bytes; } private static void check(ResponseAPDU resp) { if (resp.getSW() != 0x9000) throw new RuntimeException("PROBLEMO AMIGO!"); } private static boolean verifyKeypairIntegrity(RSAPrivateCrtKey privkey, RSAPublicKey pubkey) throws NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeyException { byte[] nonce = new byte[16]; SecureRandom sr = SecureRandom.getInstance("SHA1PRNG"); Cipher verify_cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); sr.nextBytes(nonce); verify_cipher.init(Cipher.ENCRYPT_MODE, pubkey); byte[] cryptogram = verify_cipher.doFinal(nonce); verify_cipher.init(Cipher.DECRYPT_MODE, privkey); byte[] result = verify_cipher.doFinal(cryptogram); if (!Arrays.equals(nonce, result)) return false; sr.nextBytes(nonce); verify_cipher.init(Cipher.ENCRYPT_MODE, privkey); cryptogram = verify_cipher.doFinal(nonce); verify_cipher.init(Cipher.DECRYPT_MODE, pubkey); result = verify_cipher.doFinal(cryptogram); if (!Arrays.equals(nonce, result)) return false; return true; } }