-1

I'm developing a smartcard that communicates via middleware to a server. The server needs to authenticate itself to the smartcard. Part of this authentication process is a challenge that is encrypted with a sessionkey. The server needs to decrypt the challenge with the sessionkey. This happens with the following Cipher parameters:

Algorithm: AES Mode: CBC Padding: noPadding

The code for encryption on the card:

public class Card extends Applet {

private final static byte[] PUBLIC_KEY_EXPONENT_CA = new byte[] {(byte) 0x1, 0x00, 0x01};
private final static byte[] PUBLIC_KEY_MODULUS_CA = new byte[] { (byte) 0xC0, 0x16, 0x35, 0x2C, 0x2F, 0x0E, 0x6C, 0x6B, (byte) 0x96, (byte) 0x9F, 0x53, 0x0B, 0x00, (byte) 0xE9, 0x05, 0x71, (byte) 0xCB, 0x39, (byte) 0xD0, 0x23, (byte) 0xF0, (byte) 0x80, 0x45, 0x75, 0x00, (byte) 0xCD, (byte) 0x80, (byte) 0xC7, 0x4B, 0x3A, 0x1B, 0x61, (byte) 0x86, (byte) 0xF4, 0x44, (byte) 0xCD, 0x21, 0x63, (byte) 0xCB, 0x44, 0x63, (byte) 0x9D, (byte) 0x97, 0x4F, 0x40, (byte) 0xA4, 0x1E, 0x01, 0x16, 0x39, 0x1D, (byte) 0xE5, 0x67, (byte) 0xBE, 0x01, (byte) 0xC8, (byte) 0x82, 0x52, 0x0B, 0x13, 0x71, (byte) 0xF5, 0x38, 0x21 };
private final RSAPublicKey publicKeyCA;

private final static byte[] PUBLIC_KEY_EXPONENT_SP = new byte[3];
private final static byte[] PUBLIC_KEY_MODULUS_SP = new byte[64];
private final RSAPublicKey publicKeySP;

private final static byte[] KEY_SESSION = new byte[32];
private final static byte[] CHALLENGE_SESSION = new byte[32];

private static final short LENGTH_SIGNATURE = 64;
private static final short LENGTH_TIME = 8;

private final byte[] time = new byte[LENGTH_TIME];
private final byte[] lastValidationTime = new byte[LENGTH_TIME];
private final byte[] differenceTime = new byte[LENGTH_TIME];
private final byte[] signatureBytes = new byte[LENGTH_SIGNATURE];
private final byte[] hash = new byte[20];
private final byte[] storage = new byte[331];

private final RandomData random;
private final Signature signature;
private final MessageDigest digest;
private final Cipher cipherRSA;
private final Cipher cipherAES;

private Card() {
    publicKeySP = (RSAPublicKey) KeyBuilder.buildKey(KeyBuilder.TYPE_RSA_PUBLIC, KeyBuilder.LENGTH_RSA_512, false);
    signature = Signature.getInstance(Signature.ALG_RSA_SHA_PKCS1, false);
    digest = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
    cipherRSA = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);
    cipherAES = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);

    random = RandomData.getInstance(RandomData.ALG_PSEUDO_RANDOM) ;
}

public void process(APDU apdu) throws ISOException {

     byte[] buffer = apdu.getBuffer();
     ...
     byte instruction = buffer[ISO7816.OFFSET_INS];
     switch(instruction){
     ...
     case INS_AUTHENTICATE_SP:
        authenticateSP(apdu);
        break;
     default: ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
    }
}

private void authenticateSP(APDU apdu) {

    loadSPCertificate();

    if (!publicKeySP.isInitialized()) {
        ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
    }

    verifySPCertificate();

    clear(KEY_SESSION);
    clear(CHALLENGE_SESSION);

    AESKey sessionKeySP = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_128, true);
    random.generateData(KEY_SESSION, (short) 0, (short) KEY_SESSION.length);
    sessionKeySP.setKey(KEY_SESSION, (short) 0);
    byte[] sessionKeySPBytesEncrypted = new byte[64];
    cipherRSA.init(publicKeySP, Cipher.MODE_ENCRYPT);
    cipherRSA.doFinal(KEY_SESSION, (short) 0, (short) KEY_SESSION.length, sessionKeySPBytesEncrypted, (short) 0);

    random.generateData(CHALLENGE_SESSION, (short) 0, (short) 32);

    byte[] messageBytes = new byte[128];
    byte[] messageBytesEncrypted = new byte[128];
    Util.arrayCopy(CHALLENGE_SESSION, (short) 0, messageBytes, (short) 0, (short) 32);
    Util.arrayCopy(storage, (short) 0, messageBytes, (short) 32, (short) 64);
    Util.arrayFillNonAtomic(messageBytes, (short) 96, (short) 32, (byte) -1);
    cipherAES.init(sessionKeySP, Cipher.MODE_ENCRYPT, new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, (short) 0, (short) 16);
    cipherAES.doFinal(messageBytes, (short) 0, (short) messageBytes.length, messageBytesEncrypted, (short) 0);

    apdu.setOutgoing();
    apdu.setOutgoingLength((short) (sessionKeySPBytesEncrypted.length + messageBytesEncrypted.length));
    apdu.sendBytesLong(sessionKeySPBytesEncrypted, (short) 0, (short) sessionKeySPBytesEncrypted.length);
    apdu.sendBytesLong(messageBytesEncrypted, (short) 0, (short) messageBytesEncrypted.length);
}

private void verifySPCertificate() {
    signature.init(publicKeyCA, Signature.MODE_VERIFY);

    // The signature is used on the hash of the certificate without the signature bytes
    Util.arrayCopy(storage, (short) 267, signatureBytes, (short) 0, (short) LENGTH_SIGNATURE);

    digest.doFinal(storage, (short) 0, (short) 267, hash, (short) 0);

    if (!signature.verify(hash, (short) 0, (short) hash.length, signatureBytes, (short) 0, (short) signatureBytes.length)) {
        ISOException.throwIt(SW_VERIFY_CERTIFICATE_ERROR);
    }

    Util.arrayCopy(storage, (short) 259, time, (short) 0, (short) time.length);
    byte result = BigIntNumber.compare(time, (byte) 259, lastValidationTime, (byte) 0, (byte) LENGTH_TIME);
    if (result < 0) {
        ISOException.throwIt(SW_VERIFY_CERTIFICATE_ERROR);
    }
}

private void loadSPCertificate() {
    Util.arrayCopy(storage, (short) 128, PUBLIC_KEY_EXPONENT_SP, (short) 0, (short) PUBLIC_KEY_EXPONENT_SP.length);
    Util.arrayCopy(storage, (short) 131, PUBLIC_KEY_MODULUS_SP, (short) 0, (short) PUBLIC_KEY_MODULUS_SP.length);
    publicKeySP.setExponent(PUBLIC_KEY_EXPONENT_SP, (short) 0, (short) PUBLIC_KEY_EXPONENT_SP.length);
    publicKeySP.setModulus(PUBLIC_KEY_MODULUS_SP, (short) 0, (short) PUBLIC_KEY_MODULUS_SP.length);
}
}
sessionKeySP: [89, 93, -96, 94, 97, -115, -91, -90, 100, -17, 106, -109, 18, 114, -11, 3, -53, 116, 64, 100, -96, 38, 57, -21, 52, -111, 32, -97, 58, 39, 25, -128]
messageBytes: [32, -52, -49, -20, -85, 41, 125, 116, 45, -53, 53, 20, -53, -84, -128, -67, -114, -68, 66, -96, 67, 78, 61, 115, 112, 84, 97, 120, 79, 110, 87, 101, 98, 44, 32, 68, 67, 61, 101, 71, 111, 118, 101, 114, 110, 109, 101, 110, 116, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
messageBytesEncrypted: [-15, -114, 51, 112, 32, -108, 72, 108, 4, -77, -126, 78, 89, -34, 5, 53, 51, -84, 99, 28, -16, -3, 50, 50, -31, 108, -98, -18, -113, -126, 18, 18, 72, 7, 49, -76, -7, 111, -33, 47, 18, -94, 82, -49, -75, 60, -113, -49, 80, 101, -71, -43, 29, 52, -33, 15, -71, 89, 7, 6, -15, 30, -69, 103, -14, 91, -116, -94, -99, -120, -110, 42, -76, 10, -17, -58, -77, 80, 57, 43, -128, -72, 104, -69, -9, -103, -123, -125, -98, 25, -116, 96, 13, -38, -78, 87, -18, 103, 0, 15, 0, -63, -19, 86, 31, -21, 41, -127, -127, 58, -104, 89, 41, 18, -70, -86, 98, -33, 58, 80, -12, -102, 7, -106, -14, -18, -69, 34]

The code for decryption on the server:

private byte[] verifyAuthenticationServiceProvider(byte[] encryptedData) throws Exception {

    byte[] sessionKeyBytesEncrypted = new byte[64];
    System.arraycopy(encryptedData, 0, sessionKeyBytesEncrypted, 0, 64);
    byte[] messageBytesEncrypted = new byte[128];
    System.arraycopy(encryptedData, 64, messageBytesEncrypted, 0, 128);

    Cipher cipherRSA = Cipher.getInstance("RSA/ECB/PKCS1Padding");
    cipherRSA.init(Cipher.DECRYPT_MODE, privateKey);
    byte[] sessionKeyBytes = cipherRSA.doFinal(sessionKeyBytesEncrypted);
    SecretKey sessionKey = new SecretKeySpec(sessionKeyBytes, 0, sessionKeyBytes.length, "AES");

    Cipher cipherAES = Cipher.getInstance("AES/CBC/NoPadding");
    IvParameterSpec ivspec = new IvParameterSpec(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
    cipherAES.init(Cipher.DECRYPT_MODE, sessionKey, ivspec);
    byte[] messageBytes = cipherAES.doFinal(messageBytesEncrypted);

    byte[] challengeBytes = new byte[32];
    byte[] subjectBytes = new byte[64];
    System.arraycopy(messageBytes, 0, challengeBytes, 0, 32);
    System.arraycopy(messageBytes, 32, subjectBytes, 0, 64);
    ...
}

sessionKey: [89, 93, -96, 94, 97, -115, -91, -90, 100, -17, 106, -109, 18, 114, -11, 3, -53, 116, 64, 100, -96, 38, 57, -21, 52, -111, 32, -97, 58, 39, 25, -128]
messageBytesEncrypted: [-15, -114, 51, 112, 32, -108, 72, 108, 4, -77, -126, 78, 89, -34, 5, 53, 51, -84, 99, 28, -16, -3, 50, 50, -31, 108, -98, -18, -113, -126, 18, 18, 72, 7, 49, -76, -7, 111, -33, 47, 18, -94, 82, -49, -75, 60, -113, -49, 80, 101, -71, -43, 29, 52, -33, 15, -71, 89, 7, 6, -15, 30, -69, 103, -14, 91, -116, -94, -99, -120, -110, 42, -76, 10, -17, -58, -77, 80, 57, 43, -128, -72, 104, -69, -9, -103, -123, -125, -98, 25, -116, 96, 13, -38, -78, 87, -18, 103, 0, 15, 0, -63, -19, 86, 31, -21, 41, -127, -127, 58, -104, 89, 41, 18, -70, -86, 98, -33, 58, 80, -12, -102, 7, -106, -14, -18, -69, 34]
messageBytes: [-74, -125, 17, -98, 106, -83, -100, 29, 89, 70, -87, -122, -104, 84, 36, -34, 87, -111, 10, 13, -12, -112, -48, -90, 127, 75, 95, 64, 11, -96, 25, 26, 92, -111, 10, 6, 93, 36, -95, 127, -50, 65, 15, -111, -82, 48, 2, 94, 8, -6, -102, -96, -43, -110, -57, -26, -79, -42, -57, 121, -110, -74, -108, 17, 44, -28, -43, -96, 79, 60, -91, 23, -67, -76, 10, -1, -57, -11, 10, 114, -74, 107, -99, -83, 38, 109, 99, -13, 44, 102, 112, 74, -13, -84, 60, -101, -19, -78, -50, 29, 50, 105, -10, -103, -107, -47, -60, 78, 61, -35, -12, 114, -21, -34, -110, -103, -91, -70, -73, 0, 118, 45, 113, 40, 99, -76, -8, 126]

Unfortunately, the decrypted messageBytes are not the same as the ones that were send on the card. Somehow, the decryption (on the server) is different from the encryption (on the card), but I can't understand why or how. My Cipher configuration is the same on the card, as on the server.

Note, that I checked that the used session key and IvParameters are correct both on card and server.

Also note, that I'm aware of the imposed security risks associated with this cryptographic code.

Update 1

Now, including the session key in bytes. Additionally I updated the size of the challenge to 32 bytes (because I need to encrypt it with AES afterwards again).

Update 2

Now, including all code on the card for the method authenticateSP.

Ruben Vervaeke
  • 51
  • 2
  • 11
  • Is there a reason you can't use TLS? And why are you implementing it this way if you know it contains vulnerabilities? – Luke Joshua Park May 27 '18 at 08:38
  • I am using TLS, it's just not part of the issue I'm trying to address. It's a project for school, and we need to follow a predefined algorithm for implementing the authentication protocol, as a student we need to pinpoint the risks in the implementation. – Ruben Vervaeke May 27 '18 at 08:47
  • 2
    Just a quick reminder to NOT allocate byte arrays during process() on JavaCard. – Martin Paljak May 27 '18 at 11:09
  • can you add content of key in both sides too? I'm almost sure that key is not similar in both sides... – Afshin May 27 '18 at 13:47
  • The key is the same, see update. Unless, the creation of the AESKey on the card results in a different SecretKey on the server? – Ruben Vervaeke May 27 '18 at 15:34
  • Could you please post the complete code of your applet? – vojta May 28 '18 at 12:46
  • The `sessionKeySP` seems to be generated each time `authenticateSP(...)` is called. That is wrong, isn't it? – vojta May 28 '18 at 12:48
  • @vojta yes, correct, this is also exactely what I want to achieve, since the key should survice only for the session. authenticateSP(...) is called everytime an SP wants to authenticate itself to a card. – Ruben Vervaeke May 29 '18 at 13:32

1 Answers1

0

Found the solution. The reason the decrypted bytes were not the same is that the session key on the card is set with a 256-bit (32-byte) array (KEYS_SESSION), while I specified that the AESkey should be build using a 128-bit key.

Ruben Vervaeke
  • 51
  • 2
  • 11