Unified sdk (#12)

* unifying Android and Desktop SDK

* implement desktop SDK adapter

* updating declarations

* change include syntax

* following jitpack.io guide for Android libraries

* add install task to all artefacts, add javadoc generation

* fixing javadoc

* use explicit provider "SC" instead of relying on order

* move to BouncyCastle for desktop compatibility

* improve documentation
This commit is contained in:
Bitgamma
2018-12-11 11:50:53 +03:00
committed by GitHub
parent 07fc087cb3
commit b08b1dc7ce
47 changed files with 268 additions and 103 deletions
+25 -5
View File
@@ -1,8 +1,28 @@
# Keycard Android SDK
# Keycard Java SDK for Android and Desktop
This SDK simplifies integration with the [Status Keycard](https://github.com/status-im/status-keycard) in Android
applications. In this SDK you find both the classes needed for generic communication with SmartCards as well as classes
specifically addressing the Keycard.
and Desktop applications. In this SDK you find both the classes needed for generic communication with SmartCards as well
as classes specifically addressing the Keycard.
To get started, check the file demo/src/main/java/im/status/keycard/app/MainActivity.java which a simple
demo application showing how the SDK works and what you can do with it.
To get started, check the file ```demo-android/src/main/java/im/status/keycard/app/MainActivity.java``` which a simple
demo application showing how the SDK works and what you can do with it.
## Usage
You can import the SDK in your Gradle or Maven project using [Jitpack.io](https://jitpack.io).
### On Android
```groovy
dependencies {
implementation 'com.github.status-im.status-keycard-java:android:2.0rc1'
}
```
### on the desktop
```groovy
dependencies {
implementation 'com.github.status-im.status-keycard-java:desktop:2.0rc1'
}
```
+8
View File
@@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
.idea
.DS_Store
/build
/captures
.externalNativeBuild
+50
View File
@@ -0,0 +1,50 @@
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
group = 'com.github.status-im'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 19
targetSdkVersion 28
versionCode 2
versionName "2.0"
}
}
dependencies {
api project(':lib')
}
// build a jar with source files
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
failOnError false
source = android.sourceSets.main.java.sourceFiles
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
classpath += configurations.compile
}
// build a jar with javadoc
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives sourcesJar
archives javadocJar
}
android.libraryVariants.all { variant ->
def name = variant.buildType.name
def task = project.tasks.create "jar${name.capitalize()}", Jar
task.dependsOn variant.javaCompile
task.from variant.javaCompile.destinationDir
artifacts.add('archives', task);
}
@@ -1,7 +1,10 @@
package im.status.keycard.io;
package im.status.keycard.android;
import android.nfc.tech.IsoDep;
import android.util.Log;
import im.status.keycard.io.APDUCommand;
import im.status.keycard.io.APDUResponse;
import im.status.keycard.io.CardChannel;
import java.io.IOException;
@@ -1,12 +1,14 @@
package im.status.keycard.io;
package im.status.keycard.android;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.SystemClock;
import android.util.Log;
import im.status.keycard.globalplatform.Crypto;
import im.status.keycard.io.CardListener;
import java.io.IOException;
import java.security.Security;
/**
* Manages connection of NFC-based cards. Extends Thread and must be started using the start() method. The thread has
@@ -21,6 +23,10 @@ public class NFCCardManager extends Thread implements NfcAdapter.ReaderCallback
private CardListener cardListener;
private int loopSleepMS;
static {
Crypto.addSpongyCastleProvider();
}
/**
* Constructs an NFC Card Manager with default delay between loop iterations.
*/
@@ -35,7 +41,6 @@ public class NFCCardManager extends Thread implements NfcAdapter.ReaderCallback
*/
public NFCCardManager(int loopSleepMS) {
this.loopSleepMS = loopSleepMS;
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
/**
+1 -4
View File
@@ -8,10 +8,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
}
}
@@ -6,8 +6,8 @@ android {
applicationId "im.status.keycard.demo"
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
versionCode 2
versionName "2.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -24,7 +24,7 @@ dependencies {
implementation 'com.madgag.spongycastle:core:1.58.0.0'
implementation 'com.madgag.spongycastle:prov:1.58.0.0'
implementation project(':lib')
implementation project(':android')
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
@@ -7,7 +7,7 @@ import android.util.Log;
import im.status.keycard.demo.R;
import im.status.keycard.io.CardChannel;
import im.status.keycard.io.CardListener;
import im.status.keycard.io.NFCCardManager;
import im.status.keycard.android.NFCCardManager;
import im.status.keycard.applet.*;
import org.spongycastle.util.encoders.Hex;

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Before

Width:  |  Height:  |  Size: 9.0 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+8
View File
@@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
.idea
.DS_Store
/build
/captures
.externalNativeBuild
+21
View File
@@ -0,0 +1,21 @@
apply plugin: 'java'
apply plugin: 'maven'
dependencies {
compile project(':lib')
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives sourcesJar
archives javadocJar
}
@@ -0,0 +1,50 @@
package im.status.keycard.desktop;
import im.status.keycard.globalplatform.Crypto;
import im.status.keycard.io.APDUCommand;
import im.status.keycard.io.APDUResponse;
import im.status.keycard.io.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import java.io.IOException;
/**
* Implementation of a CardChannel using the Java Smartcard I/O API,
*/
public class PCSCCardChannel implements CardChannel {
static {
Crypto.addSpongyCastleProvider();
}
private javax.smartcardio.CardChannel cardChannel;
/**
* Constructor. Wraps a Java Smartcard I/O CardChannel.
* @param cardChannel the card channel to wrap.
*/
public PCSCCardChannel(javax.smartcardio.CardChannel cardChannel) {
this.cardChannel = cardChannel;
}
@Override
public APDUResponse send(APDUCommand cmd) throws IOException {
CommandAPDU capdu = new CommandAPDU(cmd.getCla(), cmd.getIns(), cmd.getP1(), cmd.getP2(), cmd.getData(), cmd.getNeedsLE() ? 0x100 : 0x00);
ResponseAPDU rapdu;
try {
rapdu = cardChannel.transmit(capdu);
} catch (CardException e) {
throw new IOException(e);
}
return new APDUResponse(rapdu.getBytes());
}
@Override
public boolean isConnected() {
return true;
}
}
+18 -23
View File
@@ -1,26 +1,21 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 19
targetSdkVersion 28
versionCode 2
versionName "2.0"
}
task androidSourcesJar(type: Jar) {
from android.sourceSets.main.java.source
classifier = 'sources'
}
artifacts {
archives androidSourcesJar
}
}
apply plugin: 'java'
apply plugin: 'maven'
dependencies {
implementation 'com.madgag.spongycastle:core:1.58.0.0'
implementation 'com.madgag.spongycastle:prov:1.58.0.0'
implementation 'org.bouncycastle:bcprov-jdk15on:1.60'
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives sourcesJar
archives javadocJar
}
@@ -100,7 +100,7 @@ public class ApplicationInfo {
/**
* The number of remaining pairing slots. If zero is returned, no further pairing is possible.
* @return
* @return the number of remaining pairing slots
*/
public byte getFreePairingSlots() {
return freePairingSlots;
@@ -1,7 +1,7 @@
package im.status.keycard.applet;
import org.spongycastle.crypto.digests.KeccakDigest;
import org.spongycastle.math.ec.ECPoint;
import org.bouncycastle.crypto.digests.KeccakDigest;
import org.bouncycastle.math.ec.ECPoint;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
@@ -41,8 +41,8 @@ public class CardDuplicator {
* @param pin the card PIN
* @param deviceCount the number of devices which will be adding entropy for the key, including this one
*
* @throws IOException
* @throws APDUException
* @throws IOException communication error
* @throws APDUException unexpected card response
*/
public void startDuplication(CardChannel channel, Pairing pairing, String pin, int deviceCount) throws IOException, APDUException {
KeycardCommandSet cmdSet = preamble(channel, pairing, pin);
@@ -56,8 +56,8 @@ public class CardDuplicator {
* @param pairing the pairing info
* @param pin the card PIN
*
* @throws IOException
* @throws APDUException
* @throws IOException communication error
* @throws APDUException unexpected card response
*/
public byte[] exportKey(CardChannel channel, Pairing pairing, String pin) throws IOException, APDUException {
KeycardCommandSet cmdSet = preamble(channel, pairing, pin);
@@ -66,13 +66,13 @@ public class CardDuplicator {
/**
* Imports key. Must be used on all cards designated as the target for the duplication.
* @param channel
* @param pairing
* @param pin
* @param key
* @return
* @throws IOException
* @throws APDUException
* @param channel the card channel
* @param pairing the pairing info
* @param pin the user PIN
* @param key the key to import
* @return the key UID
* @throws IOException communication error
* @throws APDUException unexpected card response
*/
public byte[] importKey(CardChannel channel, Pairing pairing, String pin, byte[] key) throws IOException, APDUException {
KeycardCommandSet cmdSet = preamble(channel, pairing, pin);
@@ -84,8 +84,8 @@ public class CardDuplicator {
* exactly once, except for the device which started the backup.
*
* @param channel
* @throws IOException
* @throws APDUException
* @throws IOException communication error
* @throws APDUException unexpected card response
*/
public void addEntropy(CardChannel channel) throws IOException, APDUException {
KeycardCommandSet cmdSet = new KeycardCommandSet(channel);
@@ -1,6 +1,6 @@
package im.status.keycard.applet;
import org.spongycastle.util.encoders.Hex;
import org.bouncycastle.util.encoders.Hex;
public class Identifiers {
public static final byte[] PACKAGE_AID = Hex.decode("53746174757357616C6C6574");
@@ -4,9 +4,8 @@ import im.status.keycard.io.APDUCommand;
import im.status.keycard.io.APDUException;
import im.status.keycard.io.APDUResponse;
import im.status.keycard.io.CardChannel;
import org.spongycastle.jce.interfaces.ECPrivateKey;
import org.spongycastle.jce.interfaces.ECPublicKey;
import org.spongycastle.util.encoders.Hex;
import org.bouncycastle.jce.interfaces.ECPrivateKey;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
@@ -117,7 +116,6 @@ public class KeycardCommandSet {
/**
* Opens the secure channel. Calls the corresponding method of the SecureChannel class.
*
* @return the raw card response
* @throws IOException communication error
*/
public void autoOpenSecureChannel() throws IOException {
@@ -145,7 +143,7 @@ public class KeycardCommandSet {
SecretKey key;
try {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256", "BC");
PBEKeySpec spec = new PBEKeySpec(pairingPassword.toCharArray(), "Keycard Pairing Password Salt".getBytes(), 50000, 32 * 8);
key = skf.generateSecret(spec);
} catch (Exception e) {
@@ -141,7 +141,7 @@ public class Mnemonic {
SecretKey key;
try {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512", "BC");
PBEKeySpec spec = new PBEKeySpec(mnemonicPhrase.toCharArray(), ("mnemonic" + password).getBytes(), 2048, 512);
key = skf.generateSecret(spec);
} catch (Exception e) {
@@ -1,6 +1,6 @@
package im.status.keycard.applet;
import org.spongycastle.util.encoders.Base64;
import org.bouncycastle.util.encoders.Base64;
import java.util.Arrays;
@@ -1,13 +1,13 @@
package im.status.keycard.applet;
import org.spongycastle.asn1.x9.X9ECParameters;
import org.spongycastle.asn1.x9.X9IntegerConverter;
import org.spongycastle.crypto.ec.CustomNamedCurves;
import org.spongycastle.crypto.params.ECDomainParameters;
import org.spongycastle.math.ec.ECAlgorithms;
import org.spongycastle.math.ec.ECPoint;
import org.spongycastle.math.ec.FixedPointUtil;
import org.spongycastle.math.ec.custom.sec.SecP256K1Curve;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.asn1.x9.X9IntegerConverter;
import org.bouncycastle.crypto.ec.CustomNamedCurves;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.math.ec.ECAlgorithms;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointUtil;
import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve;
import java.math.BigInteger;
import java.util.Arrays;
@@ -28,7 +28,7 @@ public class RecoverableSignature {
static final ECDomainParameters CURVE;
static {
FixedPointUtil.precompute(CURVE_PARAMS.getG(), 6);
FixedPointUtil.precompute(CURVE_PARAMS.getG());
CURVE = new ECDomainParameters(CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(), CURVE_PARAMS.getH());
}
@@ -4,13 +4,13 @@ import im.status.keycard.io.APDUCommand;
import im.status.keycard.io.APDUException;
import im.status.keycard.io.APDUResponse;
import im.status.keycard.io.CardChannel;
import org.spongycastle.crypto.engines.AESEngine;
import org.spongycastle.crypto.macs.CBCBlockCipherMac;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.jce.ECNamedCurveTable;
import org.spongycastle.jce.interfaces.ECPublicKey;
import org.spongycastle.jce.spec.ECParameterSpec;
import org.spongycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.macs.CBCBlockCipherMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
@@ -68,17 +68,17 @@ public class SecureChannelSession {
public void generateSecret(byte[] keyData) {
try {
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDH");
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDH", "BC");
g.initialize(ecSpec, random);
KeyPair keyPair = g.generateKeyPair();
publicKey = ((ECPublicKey) keyPair.getPublic()).getQ().getEncoded(false);
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH", "BC");
keyAgreement.init(keyPair.getPrivate());
ECPublicKeySpec cardKeySpec = new ECPublicKeySpec(ecSpec.getCurve().decodePoint(keyData), ecSpec);
ECPublicKey cardKey = (ECPublicKey) KeyFactory.getInstance("ECDSA").generatePublic(cardKeySpec);
ECPublicKey cardKey = (ECPublicKey) KeyFactory.getInstance("ECDSA", "BC").generatePublic(cardKeySpec);
keyAgreement.doPhase(cardKey, true);
secret = keyAgreement.generateSecret();
@@ -116,7 +116,6 @@ public class SecureChannelSession {
* Follows the specifications from the SECURE_CHANNEL.md document.
*
* @param apduChannel the apdu channel
* @return the card response
* @throws IOException communication error
*/
public void autoOpenSecureChannel(CardChannel apduChannel) throws IOException {
@@ -155,7 +154,7 @@ public class SecureChannelSession {
sessionEncKey = new SecretKeySpec(Arrays.copyOf(keyData, SC_SECRET_LENGTH), "AES");
sessionMacKey = new KeyParameter(keyData, SC_SECRET_LENGTH, SC_SECRET_LENGTH);
sessionCipher = Cipher.getInstance("AES/CBC/ISO7816-4Padding");
sessionCipher = Cipher.getInstance("AES/CBC/ISO7816-4Padding", "BC");
sessionMac = new CBCBlockCipherMac(new AESEngine(), 128, null);
open = true;
} catch(Exception e) {
@@ -196,7 +195,7 @@ public class SecureChannelSession {
MessageDigest md;
try {
md = MessageDigest.getInstance("SHA256");
md = MessageDigest.getInstance("SHA256", "BC");
} catch(Exception e) {
throw new RuntimeException("Is BouncyCastle in the classpath?", e);
}
@@ -309,7 +308,6 @@ public class SecureChannelSession {
* Unpair all other clients
*
* @param apduChannel the apdu channel
* @return the raw card response
* @throws IOException communication error
*/
public void unpairOthers(CardChannel apduChannel) throws IOException, APDUException {
@@ -441,7 +439,7 @@ public class SecureChannelSession {
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
sessionEncKey = new SecretKeySpec(secret, "AES");
sessionCipher = Cipher.getInstance("AES/CBC/ISO7816-4Padding");
sessionCipher = Cipher.getInstance("AES/CBC/ISO7816-4Padding", "BC");
sessionCipher.init(Cipher.ENCRYPT_MODE, sessionEncKey, ivParameterSpec);
initData = sessionCipher.doFinal(initData);
byte[] encrypted = new byte[1 + publicKey.length + iv.length + initData.length];
@@ -1,10 +1,8 @@
package im.status.keycard.globalplatform;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.*;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
@@ -23,6 +21,16 @@ public class Crypto {
public static long PIN_BOUND = 999999L;
public static long PUK_BOUND = 999999999999L;
private static boolean spongyCastleLoaded = false;
public static void addSpongyCastleProvider() {
if (!spongyCastleLoaded) {
Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
Security.addProvider(new BouncyCastleProvider());
spongyCastleLoaded = true;
}
}
/**
* Derives a session key for SCP02.
*
@@ -44,7 +52,7 @@ public class Crypto {
SecretKeySpec tmpKey = new SecretKeySpec(key24, "DESede");
Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, tmpKey, new IvParameterSpec(NullBytes8));
return cipher.doFinal(derivationData);
@@ -52,6 +60,8 @@ public class Crypto {
throw new IllegalStateException("error generating session keys.", e);
} catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) {
throw new RuntimeException("error generating session keys.", e);
} catch (NoSuchProviderException e) {
throw new RuntimeException("SpongyCastle not installed");
}
}
@@ -100,7 +110,7 @@ public class Crypto {
public static byte[] mac3des(byte[] keyData, byte[] data, byte[] iv) {
try {
SecretKeySpec key = new SecretKeySpec(resizeKey24(keyData), "DESede");
Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] result = cipher.doFinal(data, 0, 24);
byte[] tail = new byte[8];
@@ -122,11 +132,11 @@ public class Crypto {
public static byte[] macFull3des(byte[] keyData, byte[] data, byte[] iv) {
try {
SecretKeySpec keyDes = new SecretKeySpec(resizeKey8(keyData), "DES");
Cipher cipherDes = Cipher.getInstance("DES/CBC/NoPadding");
Cipher cipherDes = Cipher.getInstance("DES/CBC/NoPadding", "BC");
cipherDes.init(Cipher.ENCRYPT_MODE, keyDes, new IvParameterSpec(iv));
SecretKeySpec keyDes3 = new SecretKeySpec(resizeKey24(keyData), "DESede");
Cipher cipherDes3 = Cipher.getInstance("DESede/CBC/NoPadding");
Cipher cipherDes3 = Cipher.getInstance("DESede/CBC/NoPadding", "BC");
byte[] des3Iv = iv.clone();
if (data.length > 8) {
@@ -183,7 +193,7 @@ public class Crypto {
*/
public static byte[] encryptICV(byte[] macKeyData, byte[] mac) {
try {
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding");
Cipher cipher = Cipher.getInstance("DES/ECB/NoPadding", "BC");
SecretKeySpec key = new SecretKeySpec(resizeKey8(macKeyData), "DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(mac);
@@ -1,7 +1,7 @@
package im.status.keycard.globalplatform;
import im.status.keycard.applet.Identifiers;
import org.spongycastle.util.encoders.Hex;
import org.bouncycastle.util.encoders.Hex;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -48,8 +48,8 @@ public class SecureChannel {
* @param hostChallenge the host challenge
* @param cardKeys the SCP02 keys
* @param resp the response from the card to the INITIALIZE UPDATE oommand
* @return
* @throws APDUException
* @return the Session object built on succesful verification
* @throws APDUException communication error
*/
public static Session verifyChallenge(byte[] hostChallenge, SCP02Keys cardKeys, APDUResponse resp) throws APDUException {
if (resp.getSw() == APDUResponse.SW_SECURITY_CONDITION_NOT_SATISFIED) {
+4 -2
View File
@@ -1,2 +1,4 @@
include ':lib'
include ':demo'
include 'lib'
include 'android'
include 'desktop'
include 'demo-android'