Created
May 13, 2018 16:48
-
-
Save half2me/c48154bf8514ffd5d4759e56008281ea to your computer and use it in GitHub Desktop.
Crypto Client
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.company; | |
//import org.bouncycastle.jce.ECPointUtil; | |
import javax.smartcardio.CardChannel; | |
import javax.smartcardio.CardException; | |
import javax.smartcardio.CommandAPDU; | |
import javax.smartcardio.ResponseAPDU; | |
import java.math.BigInteger; | |
import java.nio.ByteBuffer; | |
import java.nio.ByteOrder; | |
import java.security.*; | |
import java.security.spec.*; | |
public class CryptoCardClient { | |
private SecureRandom rand; | |
private CardChannel ch; | |
private static final byte CLA = 0x00; | |
private static final byte getPubKeyINS = 0x00; | |
private static final byte signChallengeINS = 0x01; | |
private static final byte pubKeyTypeP1 = 0x00; | |
private static final byte RSAPubKeyExpP1 = 0x01; | |
private static final byte RSAPubKeyModP1 = 0x02; | |
private static final byte ECPubKeyWP1 = 0x03; | |
private static final byte ECPubKeyAP1 = 0x04; | |
private static final byte ECPubKeyBP1 = 0x05; | |
private static final byte ECPubKeyGP1 = 0x06; | |
private static final byte ECPubKeyKP1 = 0x07; | |
private static final byte ECPubKeyRP1 = 0x08; | |
private static final byte ECPubKeyFieldP1 = 0x09; | |
private static CommandAPDU getKeyType() { | |
return new CommandAPDU(CLA, getPubKeyINS, pubKeyTypeP1, 0); | |
} | |
private static CommandAPDU getRSAPubKeyExp() { | |
return new CommandAPDU(CLA, getPubKeyINS, RSAPubKeyExpP1, 0); | |
} | |
private static CommandAPDU getRSAPubKeyMod() { | |
return new CommandAPDU(CLA, getPubKeyINS, RSAPubKeyModP1, 0); | |
} | |
private static CommandAPDU getECPubKeyW() { | |
return new CommandAPDU(CLA, getPubKeyINS, ECPubKeyWP1, 0); | |
} | |
private static CommandAPDU getECPubKeyA() { | |
return new CommandAPDU(CLA, getPubKeyINS, ECPubKeyAP1, 0); | |
} | |
private static CommandAPDU getECPubKeyB() { | |
return new CommandAPDU(CLA, getPubKeyINS, ECPubKeyBP1, 0); | |
} | |
private static CommandAPDU getECPubKeyG() { | |
return new CommandAPDU(CLA, getPubKeyINS, ECPubKeyGP1, 0); | |
} | |
private static CommandAPDU getECPubKeyK() { | |
return new CommandAPDU(CLA, getPubKeyINS, ECPubKeyKP1, 0); | |
} | |
private static CommandAPDU getECPubKeyR() { | |
return new CommandAPDU(CLA, getPubKeyINS, ECPubKeyRP1, 0); | |
} | |
private static CommandAPDU getECPubKeyField() { | |
return new CommandAPDU(CLA, getPubKeyINS, ECPubKeyFieldP1, 0); | |
} | |
private static CommandAPDU signChallenge(byte[] challenge) { | |
return new CommandAPDU(0, signChallengeINS, 0, 0, challenge); | |
} | |
public CryptoCardClient(CardChannel ch) { | |
this.ch = ch; | |
rand = new SecureRandom(); | |
} | |
public PublicKey getPubKey() throws CardException, NoSuchAlgorithmException, InvalidKeySpecException { | |
byte type = transmit(getKeyType()).getData()[0]; | |
switch (type) { | |
case 4: | |
// RSA | |
BigInteger exp = new BigInteger(1, transmit(getRSAPubKeyExp()).getData()); | |
BigInteger mod = new BigInteger(1, transmit(getRSAPubKeyMod()).getData()); | |
return KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(mod, exp)); | |
/*case 9: | |
// EC_F2M | |
int m = 2; // Unknown magic number... | |
BigInteger F = new BigInteger(1, transmit(getECPubKeyField()).getData()); | |
return getECPubKey(new ECFieldF2m(m, F)); | |
case 11: | |
// EC_FP | |
F = new BigInteger(1, transmit(getECPubKeyField()).getData()); | |
return getECPubKey(new ECFieldFp(F));*/ | |
default: | |
throw new InvalidKeySpecException("Unknown Key type"); | |
} | |
} | |
private ResponseAPDU transmit(CommandAPDU cmd) throws CardException { | |
return this.ch.transmit(cmd); | |
} | |
/*private PublicKey getECPubKey(ECField field) throws CardException, NoSuchAlgorithmException, InvalidKeySpecException { | |
BigInteger A = new BigInteger(1, transmit(getECPubKeyA()).getData()); | |
BigInteger B = new BigInteger(1, transmit(getECPubKeyB()).getData()); | |
BigInteger R = new BigInteger(1, transmit(getECPubKeyR()).getData()); | |
int K = (int) ByteBuffer.wrap(transmit(getECPubKeyK()).getData()).order(ByteOrder.BIG_ENDIAN).getShort(); | |
byte[] W = transmit(getECPubKeyW()).getData(); | |
byte[] G = transmit(getECPubKeyG()).getData(); | |
EllipticCurve curve = new EllipticCurve(field, A, B); | |
ECPoint w = ECPointUtil.decodePoint(curve, W); | |
ECPoint g = ECPointUtil.decodePoint(curve, G); | |
ECParameterSpec domainParams = new ECParameterSpec(curve, g, R, K); | |
return KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(w, domainParams)); | |
}*/ | |
public boolean validate(PublicKey pub) throws CardException, NoSuchAlgorithmException, InvalidKeyException, SignatureException { | |
byte[] challenge = new byte[127]; | |
rand.nextBytes(challenge); | |
byte[] response = transmit(signChallenge(challenge)).getData(); | |
Signature verifier; | |
switch (pub.getAlgorithm()) { | |
case "RSA": | |
verifier = Signature.getInstance("SHA1withRSA"); | |
break; | |
case "EC": | |
verifier = Signature.getInstance("SHA1withECDSA"); | |
break; | |
default: | |
throw new NoSuchAlgorithmException("Public key has unknown algorithm type"); | |
} | |
verifier.initVerify(pub); | |
verifier.update(challenge); | |
return verifier.verify(response); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.company; | |
import javax.smartcardio.*; | |
import java.security.InvalidKeyException; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.PublicKey; | |
import java.security.SignatureException; | |
import java.security.spec.InvalidKeySpecException; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Scanner; | |
public class Main { | |
HashMap<PublicKey, String> db; | |
private CardTerminal t; | |
private byte[] aid = {(byte)0xA0, 0x00, 0x00, 0x06, 0x17, 0x00, 0x3E, (byte) 0xef, 0x14, 0x09, 0x01}; | |
public Main(CardTerminal t) { | |
this.t = t; | |
db = new HashMap<PublicKey, String>(); | |
System.out.println("Using terminal: " + t.getName()); | |
} | |
private CardChannel waitForCard() throws CardException { | |
System.out.println("Tap card!"); | |
t.waitForCardPresent(0); | |
Card card = t.connect("*"); | |
CardChannel channel = card.getBasicChannel(); | |
ResponseAPDU response = selectApplet(channel); | |
byte[] dat = response.getBytes(); | |
if (dat[0] != -112) { | |
card.disconnect(false); | |
throw new CardException("Card does not have app installed!"); | |
} | |
return channel; | |
} | |
public boolean registerNewCard(String name) throws CardException, InvalidKeySpecException, NoSuchAlgorithmException, SignatureException, InvalidKeyException { | |
CryptoCardClient client = new CryptoCardClient(waitForCard()); | |
PublicKey key = client.getPubKey(); | |
if (client.validate(key)) { | |
db.put(key, name); | |
return true; | |
} | |
return false; | |
} | |
public boolean deleteCard(String name) { | |
PublicKey key = null; | |
for (Map.Entry<PublicKey, String> card: db.entrySet()) { | |
if (card.getValue().equals(name)) { | |
key = card.getKey(); | |
break; | |
} | |
} | |
if (key != null) { | |
db.remove(key); | |
return true; | |
} | |
return false; | |
} | |
public String identifyCard() throws Exception { | |
CryptoCardClient client = new CryptoCardClient(waitForCard()); | |
PublicKey key = client.getPubKey(); | |
if (client.validate(key)) { return db.get(key); } | |
throw new Exception("Invalid Signature"); | |
} | |
public void mainLoop() { | |
String choice; | |
while(true) { | |
try { | |
Scanner in = new Scanner(System.in); | |
System.out.println("------------------"); | |
System.out.println("Please choose an action:"); | |
System.out.println("[A] Authenticate"); | |
System.out.println("[R] Register"); | |
System.out.println("[D] Delete"); | |
System.out.println("------------------"); | |
choice = in.nextLine(); | |
switch (choice) { | |
case "A": | |
String person = identifyCard(); | |
if (person != null) { | |
System.out.println("Authenticated " + person + "!"); | |
} else { | |
System.out.println("Unknown card!"); | |
}; | |
break; | |
case "R": | |
System.out.println("Please type a name:"); | |
String name = in.nextLine(); | |
if (registerNewCard(name)) { | |
System.out.println("Successfully registered " + name + "!"); | |
} else { | |
System.out.println("Registration failed!"); | |
} | |
break; | |
case "D": | |
System.out.println("Please type a name:"); | |
String delName = in.nextLine(); | |
if (deleteCard(delName)) { | |
System.out.println("Successfully removed " + delName + "!"); | |
} else { | |
System.out.println("Deletion failed!"); | |
} | |
break; | |
} | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
public static void main(String[] args) throws CardException { | |
TerminalFactory factory = TerminalFactory.getDefault(); | |
List<CardTerminal> terminals = factory.terminals().list(); | |
if (terminals != null && !terminals.isEmpty()) { | |
// Use the first terminal | |
Main m = new Main(terminals.get(0)); | |
m.mainLoop(); | |
} else { | |
System.out.println("No pcsc terminal found"); | |
} | |
} | |
private ResponseAPDU selectApplet(CardChannel ch) throws CardException { | |
return ch.transmit(new CommandAPDU(0x00, 0xA4, 0x04, 0x00, aid)); | |
} | |
private void userInput(String msg) | |
{ | |
System.out.println("Press Enter " + msg); | |
try { System.in.read(); } catch(Exception e) {} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment