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.