add PUT KEY command
This commit is contained in:
@@ -121,6 +121,21 @@ public class Crypto {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] ecb3des(byte[] key, byte[] data) {
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding", "BC");
|
||||
SecretKeySpec keyDes = new SecretKeySpec(resizeKey24(key), "DES");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keyDes);
|
||||
return cipher.doFinal(data);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new RuntimeException("Could not encrypt data", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] kcv3des(byte[] key) {
|
||||
return Arrays.copyOf(ecb3des(key, NullBytes8), 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a 3DES MAC for SCP02 communication
|
||||
*
|
||||
|
||||
@@ -24,6 +24,7 @@ public class GlobalPlatformCommandSet {
|
||||
static final byte INS_DELETE = (byte) 0xE4;
|
||||
static final byte INS_INSTALL = (byte) 0xE6;
|
||||
static final byte INS_LOAD = (byte) 0xE8;
|
||||
static final byte INS_PUT_KEY = (byte) 0xD8;
|
||||
|
||||
static final byte SELECT_P1_BY_NAME = (byte) 0x04;
|
||||
static final byte EXTERNAL_AUTHENTICATE_P1 = (byte) 0x01;
|
||||
@@ -46,7 +47,7 @@ public class GlobalPlatformCommandSet {
|
||||
*/
|
||||
public GlobalPlatformCommandSet(CardChannel apduChannel) {
|
||||
this.apduChannel = apduChannel;
|
||||
this.cardKeys = new SCP02Keys(testKey, testKey);
|
||||
this.cardKeys = new SCP02Keys(testKey, testKey, testKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,6 +118,49 @@ public class GlobalPlatformCommandSet {
|
||||
externalAuthenticate(hostChallenge).checkOK();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a PUT KEY APDU to load or replace SCP02 keys. The keys are assumed to be 3DES keys
|
||||
*
|
||||
* @param encKey the ENC key to load
|
||||
* @param macKey the MAC key to load
|
||||
* @param dekKey the DEK key to load
|
||||
* @param oldKvn the KVN to replace, 0 to put a new key without replacing
|
||||
* @param newKvn the KVN of the new keyset
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public APDUResponse putSCP02Keys(byte[] encKey, byte[] macKey, byte[] dekKey, int oldKvn, int newKvn) throws IOException {
|
||||
if (encKey.length != 16 || macKey.length != 16 || dekKey.length != 16){
|
||||
throw new IllegalArgumentException("All keys must be 16-byte 3DES keys");
|
||||
}
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
bos.write(newKvn);
|
||||
writeSCP02Key(bos, encKey);
|
||||
writeSCP02Key(bos, macKey);
|
||||
writeSCP02Key(bos, dekKey);
|
||||
|
||||
APDUCommand cmd = new APDUCommand(0x84, INS_PUT_KEY, oldKvn, 0, bos.toByteArray());
|
||||
return this.secureChannel.send(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* writes an encrypted key for the PUT KEY command
|
||||
* @param bos the output stream to write to
|
||||
* @param key the key to encrypt and write
|
||||
* @throws IOException if the ByteArrayOutputStream throws it (never)
|
||||
*/
|
||||
private void writeSCP02Key(ByteArrayOutputStream bos, byte[] key) throws IOException {
|
||||
byte[] encrypted = Crypto.ecb3des(session.getKeys().getDekKeyData(), key);
|
||||
byte[] kcv = Crypto.kcv3des(key);
|
||||
|
||||
bos.write(0x80);
|
||||
bos.write(encrypted.length);
|
||||
bos.write(encrypted);
|
||||
bos.write(kcv.length);
|
||||
bos.write(kcv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the Keycard applet instance.
|
||||
*
|
||||
|
||||
@@ -6,14 +6,16 @@ package im.status.keycard.globalplatform;
|
||||
public class SCP02Keys {
|
||||
public byte[] encKeyData;
|
||||
public byte[] macKeyData;
|
||||
public byte[] dekKeyData;
|
||||
|
||||
/**
|
||||
* Constructor. Takes the ENC and MAC keys.
|
||||
*
|
||||
* @param encKeyData encryption key
|
||||
* @param macKeyData mac key
|
||||
* @param dekKeyData data encryption key
|
||||
*/
|
||||
public SCP02Keys(byte[] encKeyData, byte[] macKeyData) {
|
||||
public SCP02Keys(byte[] encKeyData, byte[] macKeyData, byte[] dekKeyData) {
|
||||
this.encKeyData = encKeyData;
|
||||
this.macKeyData = macKeyData;
|
||||
}
|
||||
@@ -34,4 +36,13 @@ public class SCP02Keys {
|
||||
public byte[] getMacKeyData() {
|
||||
return macKeyData;
|
||||
}
|
||||
|
||||
/**
|
||||
* The DEK key
|
||||
*
|
||||
* @return the DEK key
|
||||
*/
|
||||
public byte[] getDekKeyData() {
|
||||
return dekKeyData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import im.status.keycard.io.APDUResponse;
|
||||
import im.status.keycard.io.CardChannel;
|
||||
|
||||
/**
|
||||
* An SCP02 Secure Channel. Wraps a CardChannel to allow transparent handling of the scure channel.
|
||||
* An SCP02 Secure Channel. Wraps a CardChannel to allow transparent handling of the secure channel.
|
||||
*/
|
||||
public class SecureChannel {
|
||||
private CardChannel channel;
|
||||
@@ -77,8 +77,9 @@ public class SecureChannel {
|
||||
|
||||
byte[] sessionEncKey = Crypto.deriveSCP02SessionKey(cardKeys.getEncKeyData(), seq, DERIVATION_PURPOSE_ENC);
|
||||
byte[] sessionMacKey = Crypto.deriveSCP02SessionKey(cardKeys.getMacKeyData(), seq, DERIVATION_PURPOSE_MAC);
|
||||
byte[] sessionDekKey = Crypto.deriveSCP02SessionKey(cardKeys.getDekKeyData(), seq, DERIVATION_PURPOSE_DEK);
|
||||
|
||||
SCP02Keys sessionKeys = new SCP02Keys(sessionEncKey, sessionMacKey);
|
||||
SCP02Keys sessionKeys = new SCP02Keys(sessionEncKey, sessionMacKey, sessionDekKey);
|
||||
|
||||
boolean verified = Crypto.verifyCryptogram(sessionKeys.getEncKeyData(), hostChallenge, cardChallenge, cardCryptogram);
|
||||
if (!verified) {
|
||||
|
||||
Reference in New Issue
Block a user