Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support jcardsim and J3H081 #31

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ant-javacard.jar
doc
*.swp
*.orig
*.un~
*.cap
*.jar
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,31 @@ After cloning the IsoApplet repository, all you have to do is:
# Installation
Install the CAP-file (IsoApplet.cap) to your Java Card smart card (e.g. with [GlobalPlatformPro](https://github.com/martinpaljak/GlobalPlatformPro)).

# Using with jcardsim
Make sure to install the appropriate vsmartcard package. On Debian/Ubuntu, this is called vsmartcard-vpcd. You may need to restart pcscd after installing it. Note: This is only appropriate for development machines; it creates an open listening socket with no authentication that pcscd exposes as the first reader so it is trivial to MitM for an attacker.

Setup a jcardsim.cfg file like so:
```
com.licel.jcardsim.card.applet.0.AID=F276A288BCFBA69D34F31001
com.licel.jcardsim.card.applet.0.Class=xyz.wendland.javacard.pki.isoapplet.IsoApplet
com.licel.jcardsim.terminal.type=2
com.licel.jcardsim.vsmartcard.host=127.0.0.1
com.licel.jcardsim.vsmartcard.port=35963
```

Run jcardsim with the IsoApplet.jar file on the path:
```java -cp IsoApplet.jar:jcardsim-3.0.5-SNAPSHOT.jar com.licel.jcardsim.remote.VSmartCard jcardsim.cfg```

Instantiate the applet in jcardsim (this example uses gpshell):
```
echo 'establish_context
card_connect
send_apdu -sc 0 -APDU 80b800000d0cF276A288BCFBA69D34F31001
card_disconnect
release_context' | gpshell
```

Now you should be able to use pkcs15-init and friends.

**Have a look at the wiki for more information:** https://github.com/philipWendland/IsoApplet/wiki

2 changes: 1 addition & 1 deletion build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<target name="dist" description="generate the distribution">
<tstamp/>
<javacard jckit="ext/sdks/jc310r20210706_kit">
<cap targetsdk="ext/sdks/jc304_kit" aid="f2:76:a2:88:bc:fb:a6:9d:34:f3:10" output="IsoApplet.cap" sources="src" version="1.0">
<cap targetsdk="ext/sdks/jc304_kit" aid="f2:76:a2:88:bc:fb:a6:9d:34:f3:10" output="IsoApplet.cap" sources="src" version="1.0" jar="IsoApplet.jar">
<applet class="xyz.wendland.javacard.pki.isoapplet.IsoApplet" aid="f2:76:a2:88:bc:fb:a6:9d:34:f3:10:01"/>
</cap>
</javacard>
Expand Down
110 changes: 62 additions & 48 deletions src/xyz/wendland/javacard/pki/isoapplet/IsoApplet.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.APDU;
import javacard.framework.SystemException;
import javacard.framework.TransactionException;
import javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.framework.OwnerPIN;
Expand Down Expand Up @@ -61,6 +63,11 @@ public class IsoApplet extends Applet implements ExtendedLength {
/* Card-specific configuration */
public static final boolean DEF_PRIVATE_KEY_IMPORT_ALLOWED = false;

/* Certain cards (J3H081) break in unexpected ways testing these at
* runtime. Allow them to be forced off so no test is run. */
public static final boolean DEF_TEST_RSA_4096 = true;
public static final boolean DEF_TEST_RSA_PSS = true;

/* ISO constants not in the "ISO7816" interface */
// File system related INS:
public static final byte INS_CREATE_FILE = (byte) 0xE0;
Expand Down Expand Up @@ -138,12 +145,6 @@ public class IsoApplet extends Applet implements ExtendedLength {
private Key[] keys = null;
private byte[] ram_buf = null;
private Cipher rsaPkcs1Cipher = null;
private Signature ecdsaSignature = null;
private Signature rsaSha1PssSignature = null;
private Signature rsaSha224PssSignature = null;
private Signature rsaSha256PssSignature = null;
private Signature rsaSha384PssSignature = null;
private Signature rsaSha512PssSignature = null;
private RandomData randomData = null;
private byte api_features;

Expand Down Expand Up @@ -179,54 +180,60 @@ protected IsoApplet() {

// API features: probe card support for ECDSA
try {
ecdsaSignature = Signature.getInstance(MessageDigest.ALG_NULL, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL, false);
Signature.getInstance(MessageDigest.ALG_NULL, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL, false);
api_features |= API_FEATURE_ECC;
} catch (CryptoException e) {
if(e.getReason() == CryptoException.NO_SUCH_ALGORITHM) {
/* Few Java Cards do not support ECDSA at all.
* We should not throw an exception in this cases
* as this would prevent installation. */
ecdsaSignature = null;
api_features &= ~API_FEATURE_ECC;
} else {
throw e;
}
}

// API features: probe card support for 4096 bit RSA keys
try {
RSAPrivateCrtKey testKey = (RSAPrivateCrtKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, KeyBuilder.LENGTH_RSA_4096, false);
api_features |= API_FEATURE_RSA_4096;
} catch (CryptoException e) {
if(e.getReason() == CryptoException.NO_SUCH_ALGORITHM) {
api_features &= ~API_FEATURE_RSA_4096;
} else {
throw e;
if (DEF_TEST_RSA_4096) {
try {
RSAPrivateCrtKey testKey = (RSAPrivateCrtKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_CRT_PRIVATE, KeyBuilder.LENGTH_RSA_4096, false);
api_features |= API_FEATURE_RSA_4096;
} catch (CryptoException e) {
if(e.getReason() == CryptoException.NO_SUCH_ALGORITHM) {
api_features &= ~API_FEATURE_RSA_4096;
} else {
throw e;
}
} catch (TransactionException e) {
// J3H081 from javacardsdk.com (FUTAKO) raises this exception instead.
if(e.getReason() == TransactionException.INTERNAL_FAILURE) {
api_features &= ~API_FEATURE_RSA_4096;
} else {
throw e;
}
}
}


/* API features: probe card support for RSA and PSS padding with SHA-1 and all SHA-2 algorithms
* to be used with Signature.signPreComputedHash() */
try {
rsaSha1PssSignature = Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1_PSS, false);
rsaSha224PssSignature = Signature.getInstance(Signature.ALG_RSA_SHA_224_PKCS1_PSS, false);
rsaSha256PssSignature = Signature.getInstance(Signature.ALG_RSA_SHA_256_PKCS1_PSS, false);
rsaSha384PssSignature = Signature.getInstance(Signature.ALG_RSA_SHA_384_PKCS1_PSS, false);
rsaSha512PssSignature = Signature.getInstance(Signature.ALG_RSA_SHA_512_PKCS1_PSS, false);
api_features |= API_FEATURE_RSA_PSS;
} catch (CryptoException e) {
if(e.getReason() == CryptoException.NO_SUCH_ALGORITHM) {
/* Certain Java Cards do not support this algorithm.
* We should not throw an exception in this cases
* as this would prevent installation. */
rsaSha1PssSignature = null;
rsaSha224PssSignature = null;
rsaSha384PssSignature = null;
rsaSha256PssSignature = null;
rsaSha512PssSignature = null;
api_features &= ~API_FEATURE_RSA_PSS;
} else {
throw e;
if (DEF_TEST_RSA_PSS) {
try {
Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1_PSS, false);
Signature.getInstance(Signature.ALG_RSA_SHA_224_PKCS1_PSS, false);
Signature.getInstance(Signature.ALG_RSA_SHA_256_PKCS1_PSS, false);
Signature.getInstance(Signature.ALG_RSA_SHA_384_PKCS1_PSS, false);
Signature.getInstance(Signature.ALG_RSA_SHA_512_PKCS1_PSS, false);
api_features |= API_FEATURE_RSA_PSS;
} catch (CryptoException e) {
if(e.getReason() == CryptoException.NO_SUCH_ALGORITHM) {
/* Certain Java Cards do not support this algorithm.
* We should not throw an exception in this cases
* as this would prevent installation. */
api_features &= ~API_FEATURE_RSA_PSS;
} else {
throw e;
}
}
}

Expand Down Expand Up @@ -1101,7 +1108,7 @@ public void processManageSecurityEnvironment(APDU apdu) throws ISOException {
if(privKeyRef < 0) {
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
if(algRef == ALG_GEN_EC && ecdsaSignature == null) {
if(algRef == ALG_GEN_EC && (api_features & API_FEATURE_ECC) == 0) {
// There are cards that do not support ECDSA at all.
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
}
Expand Down Expand Up @@ -1129,7 +1136,7 @@ public void processManageSecurityEnvironment(APDU apdu) throws ISOException {
if(keys[privKeyRef].getType() != KeyBuilder.TYPE_EC_FP_PRIVATE) {
ISOException.throwIt(ISO7816.SW_DATA_INVALID);
}
if(ecdsaSignature == null) {
if((api_features & API_FEATURE_ECC) == 0) {
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
}

Expand Down Expand Up @@ -1297,24 +1304,27 @@ private void computeDigitalSignature(APDU apdu) throws ISOException {
} else if (currentAlgorithmRef[0] == ALG_RSA_PAD_PSS) {
// ALG_RSA_PAD_PSS with pre-computed hash.
// Determine Signature object by hash length.
Signature obj = null;
if(lc == (short) 20) {
rsaSha1PssSignature.init(rsaKey, Signature.MODE_SIGN);
sigLen = rsaSha1PssSignature.signPreComputedHash(ram_buf, (short)0, lc, ram_buf, lc);
obj = Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1_PSS, false);
} else if (lc == (short) 28) {
rsaSha224PssSignature.init(rsaKey, Signature.MODE_SIGN);
sigLen = rsaSha224PssSignature.signPreComputedHash(ram_buf, (short)0, lc, ram_buf, lc);
obj = Signature.getInstance(Signature.ALG_RSA_SHA_224_PKCS1_PSS, false);
} else if (lc == (short) 32) {
rsaSha256PssSignature.init(rsaKey, Signature.MODE_SIGN);
sigLen = rsaSha256PssSignature.signPreComputedHash(ram_buf, (short)0, lc, ram_buf, lc);
obj = Signature.getInstance(Signature.ALG_RSA_SHA_256_PKCS1_PSS, false);
} else if (lc == (short) 48) {
rsaSha384PssSignature.init(rsaKey, Signature.MODE_SIGN);
sigLen = rsaSha384PssSignature.signPreComputedHash(ram_buf, (short)0, lc, ram_buf, lc);
obj = Signature.getInstance(Signature.ALG_RSA_SHA_384_PKCS1_PSS, false);
} else if (lc == (short) 64) {
rsaSha512PssSignature.init(rsaKey, Signature.MODE_SIGN);
sigLen = rsaSha512PssSignature.signPreComputedHash(ram_buf, (short)0, lc, ram_buf, lc);
obj = Signature.getInstance(Signature.ALG_RSA_SHA_512_PKCS1_PSS, false);
} else {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}

obj.init(rsaKey, Signature.MODE_SIGN);
sigLen = obj.signPreComputedHash(ram_buf, (short)0, lc, ram_buf, lc);

if(JCSystem.isObjectDeletionSupported()) {
JCSystem.requestObjectDeletion();
}
} else {
ISOException.throwIt(ISO7816.SW_FUNC_NOT_SUPPORTED);
}
Expand All @@ -1331,8 +1341,12 @@ private void computeDigitalSignature(APDU apdu) throws ISOException {
// Get the key - it must be a EC private key,
// checks have been done in MANAGE SECURITY ENVIRONMENT.
ECPrivateKey ecKey = (ECPrivateKey) keys[currentPrivateKeyRef[0]];
Signature ecdsaSignature = Signature.getInstance(MessageDigest.ALG_NULL, Signature.SIG_CIPHER_ECDSA, Cipher.PAD_NULL, false);
ecdsaSignature.init(ecKey, Signature.MODE_SIGN);
sigLen = ecdsaSignature.sign(ram_buf, (short)0, lc, apdu.getBuffer(), (short)0);
if(JCSystem.isObjectDeletionSupported()) {
JCSystem.requestObjectDeletion();
}
apdu.setOutgoingAndSend((short) 0, sigLen);
break;

Expand Down