5

I am trying to encrypt an ISO-0 pinblock using the codenameone BouncyCastle lib. The methods I use to achieve this is as follows:

private static byte[] performEncrypt(byte[] key, String plainText, boolean padding) {
    byte[] ptBytes = plainText.getBytes();

    BufferedBlockCipher cipher;
    if (padding) {
        cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()));
    } else {
        cipher = new BufferedBlockCipher(new CBCBlockCipher(new DESedeEngine()));
    }
    cipher.init(true, new KeyParameter(key));
    byte[] rv = new byte[cipher.getOutputSize(ptBytes.length)];
    int oLen = cipher.processBytes(ptBytes, 0, ptBytes.length, rv, 0);
    try {
        cipher.doFinal(rv, oLen);
    } catch (CryptoException ce) {
        LoggingUtil.error(TAG, ce, "Unexpected Exception");
    }
    return rv;
}

private static String createIso0PinBlock(String pin, String number) {
    ...
}

private static String getPaddedData(String data, byte padCharacter) {
    String paddedData = ByteUtil.pad(data, (char) padCharacter, 8).toString();
    return paddedData;
}

public static String createPinBlockAndEncrypt(String pin, String number) {
    LoggingUtil.debug("SecurityUtil", "CREAT PIN BLOCK AND ENCRYPT.. PIN: " + pin + " NUMBER: " + number);
    String pb = createIso0PinBlock(pin, number.substring(0, number.length() - 1));
    LoggingUtil.debug("SecurityUtil", "PINBLOCK: " + pb);
    String padded = getPaddedData(pb, (byte) 0x00);
    LoggingUtil.debug("SecurityUtil", "PADDED: " + padded);
    byte[] encrypted = performEncrypt(Hex.decode(KEY.getBytes()), new String(ByteUtil.hex2byte(padded)), false);
    return ByteUtil.byte2hex(encrypted);
}

In ByteUtil:

public static StringBuilder pad(String data, char padCharacter, int multiplier) {
    StringBuilder text = new StringBuilder();
    text.append(data);
    while (text.length() % multiplier != 0) {
        text.append(padCharacter);
    }
    return text;
}

To give example Log outputs:

[SecurityUtil] CREAT PIN BLOCK AND ENCRYPT.. PIN: 2255 NUMBER: 6284734104205417486
[SecurityUtil] PINBLOCK: 042214FBDFABE8B7
[SecurityUtil] PADDED: 042214FBDFABE8B7

When I run this through a public static void main method, it works as expected, however, when I build this for Android via Codenameone, I get the following error in logcat:

org.bouncycastle.crypto.DataLengthException: data not block size aligned
org.bouncycastle.crypto.BufferedBlockCipher.doFinal(BufferedBlockCipher.java:275)

Despite the padded pinblock being 16 in length (a multiple of 8).

Any help regarding this issue would be appreciated.

Propagandian
  • 444
  • 3
  • 10

1 Answers1

2

Encryption works on binary data, and your pinblock is binary, so keep it that way.

When calling performEncrypt(..) you convert your hex encoded pinblock to a string with new String(ByteUtil.hex2byte(padded)), and inside performEncrypt(...) you convert it to a byte array with byte[] ptBytes = plainText.getBytes();. The problem with this is that not all byte sequences can be mapped correctly back and forth through a string, and you might end up with different data and even different length etc. take a look here

Change your signature of you performEncrypt(..) to:

private static byte[] performEncrypt(byte[] key, byte[] plainText, boolean padding) {

and avoid converting through a string altogether.

Community
  • 1
  • 1
Ebbe M. Pedersen
  • 7,250
  • 3
  • 27
  • 47
  • Great answer. I would avoid using Strings for binary data in Codename One as we map Strings to native Strings e.g. on iOS and they might have slight behavioral discrepancies in some extreme edge cases. E.g. we had an issue when integrating zip support https://www.codenameone.com/blog/zip-and-toast.html – Shai Almog Jul 23 '16 at 04:55