Skip to content

Instantly share code, notes, and snippets.

@half2me
Created May 13, 2018 16:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save half2me/c48154bf8514ffd5d4759e56008281ea to your computer and use it in GitHub Desktop.
Save half2me/c48154bf8514ffd5d4759e56008281ea to your computer and use it in GitHub Desktop.
Crypto Client
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);
}
}
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