0

I have written a java card applet to RSA encrypt incoming data with private key and send it out again.

Here is my code:

package test;

import javacard.framework.*;
import javacard.security.*;
import javacardx.crypto.Cipher;

public class test extends Applet
{
    private static byte[] Hash = new byte[32];
    private static byte[] Sign = new byte[256];
    private static short hash_len = 0;
    private static short sign_len = 0;
    MessageDigest mDigest = MessageDigest.getInstance(MessageDigest.ALG_SHA_256,false);
    Cipher rsaCipher = Cipher.getInstance(Cipher.ALG_RSA_NOPAD, false);
    KeyPair rsaKey = new KeyPair(KeyPair.ALG_RSA,KeyBuilder.LENGTH_RSA_2048);
    RSAPublicKey rsaPubKey = (RSAPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_2048, false);

    public static void install(byte[] bArray, short bOffset, byte bLength) 
    {
        new test().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
    }

    public void process(APDU apdu)
    {
        if (selectingApplet())
        {
            return;
        }

        byte[] buf = apdu.getBuffer();
        switch (buf[ISO7816.OFFSET_INS])
        {
        case (byte)0x00: // generate keypair
            rsaKey.genKeyPair();
            break;
        case (byte)0x01: // get public key
            apdu.setIncomingAndReceive();
            rsaPubKey = (RSAPublicKey) rsaKey.getPublic();
            rsaPubKey.getModulus(buf, (short) 0);
            apdu.setOutgoingAndSend((short) 0, (short) 256);
            break;
        case (byte)0x03: //encrypt by private key
            short len = apdu.setIncomingAndReceive();
            mDigest.reset();
            hash_len = mDigest.doFinal(buf, (short) ISO7816.OFFSET_CDATA, len, Hash, (short)0);
            rsaCipher.init(rsaKey.getPrivate(), Cipher.MODE_ENCRYPT);
            sign_len = rsaCipher.doFinal(Hash, (short) 0, hash_len, Sign, (short)0);
            Util.arrayCopy(Hash, (short)0, buf, (short)0, hash_len);
            apdu.setOutgoingAndSend((short) 0, hash_len);
            break;
        default:
            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }
    }

}

I make keys successfully, but when I try to do the encryption, 6F 00 is returned in line sign_len = rsaCipher.doFinal(Hash, (short) 0, hash_len, Sign, (short)0);.

Now my first question is How can I find out the meaning of 6F 00 SWs, i.e. How to handle run-time exceptions in my code and my second question is how to fix it and what is the problem? Where am I mistaken? (I am using java card kit 3.0.4.)

MJay
  • 987
  • 1
  • 13
  • 36

1 Answers1

2

You are using Cipher.ALG_RSA_NOPAD algorithm (also known as 'raw RSA' or 'X.509 RSA') which simply does the modular exponentiation without any padding.

This algorithm requires input to be of the same length as the RSA key modulus (2048 bits / 256 bytes) and is insecure when used without proper input padding.

Use Cipher.ALG_RSA_PKCS1_OAEP (if your card supports it) or Cipher.ALG_RSA_PKCS1 for RSA encryption.


The status word 6F 00 is given because of uncaught CryptoException (see documentation for Cipher.doFinal()). You might want to surround your code with try { ... } catch (CryptoException e) { ... } and get more details about problem that has happened using CardRuntimeException.getReason().


Note: In you code you are encrypting a SHA-256 hash of data which does not make much sense -- did you want to sign the data? You should use Signature.ALG_RSA_SHA_256_PKCS1_PSS or Signature.ALG_RSA_SHA_256_PKCS1 instead (note that you do not need to do the hashing in this case as SHA-256 is computed during the signature computation).

Good luck!

vlp
  • 7,811
  • 2
  • 23
  • 51
  • so how can I send 256 bytes of data by an apdu? – MJay Jul 03 '19 at 04:54
  • 2
    @MJay Without extended length APDU (which must be supported by both card and reader) Lc limit is 255 bytes. So you must use some sort of command chaining (see the last paragraph of [this answer](https://stackoverflow.com/a/33027761/5128464) for inspiration). Technically, if you always expect 256 bytes of data, you might encode the extra 1 byte into P1 or P2, but this approach is not very clean.... – vlp Jul 03 '19 at 10:12
  • 2
    ...Also note that for longer Lc you might not be able to receive all input data in a single `APDU.setIncomingAndReceive()` call because of limited APDU buffer size (you would need to use `APDU.receiveBytes()` multiple times)... – vlp Jul 03 '19 at 10:12
  • 2
    ...You did not mention what your task is, but you will not be able to safely encrypt 256 bytes with 2048 bit key. You definitely should use ALG_RSA_PKCS1 (or ALG_RSA_PKCS1_OAEP) for encryption which cannot handle 256 bytes of input. Do not use raw RSA if you do not know what you are doing. ALG_RSA_PKCS1 allows dynamic plaintext size so if your plaintext length is up to circa 245 bytes (for 2048 bit key) use it directly to encrypt input data.... – vlp Jul 03 '19 at 10:29
  • 2
    ...Quite common scheme for encryption of longer data is to RSA-encrypt a random symmetric (AES, DES?) key and encrypt the data using this symmetric key (ideally using some authenticated encryption (AE) mode -- e.g. GCM, EAX, Encrypt-Then-Mac) and random IV. This way you can encrypt much longer data (up to the limit of single symmetric key reuse) – vlp Jul 03 '19 at 10:29
  • my task is to save a master key in a card which is delivered encrypted by a public key and return it whenever needed encrypted by private key in the card. Those are RSA key pairs generated inside the card as in code above case `0x00`. Thank you for answering me – MJay Jul 03 '19 at 13:06
  • My master key is a 256 bytes key which should be stored it inside the card. I also authenticate card when injecting master key and authenticate console when giving the key back. – MJay Jul 03 '19 at 13:11
  • Authentication is done by a pre-shared secret between card and console, doing message digest and the RSA encryption/decryption between these two entities. – MJay Jul 03 '19 at 13:12
  • 2
    @MJay If your imported master key is an RSA key you will probably need to transfer all it's CRT parameters (which is much more than 256 bytes for 2048 bit key). Then it makes sense to encrypt CRT parameters with random symmetric key (using e.g. ALG_DES_CBC_ISO9797_M2) and wrap this random symmetric key using RSA (e.g. ALG_RSA_PKCS1). Consider some integrity protection to avoid encryption without authentication (as you already have RSA involved your card might sign encrypted CRT parameters). I am no crypto expert so please do validate my thoughts... – vlp Jul 03 '19 at 16:00