1

I take a data string = "AkhilRanjanBiharabcdefghijklmnopMovedtoChennai18", encrypt it first and then decrypt it. The string which I get back on decryption is "AkhilRanjanBiharÙ†+™¸„À–ýæó@Movedtoñhennai18" which is almost fine for the first 16 and final 16 characters, but the 16 characters in the middle are absolute junk. What can possibly be going wrong?

My encryption code:-

public String encrypt(String value) {
    log.info("This method is not going to be used");
    String key = "theabcd@heymaths";
    initVector = "{{{{{{{{{{{{{{{{";
    String encryptedStr="";
    byte[] encrBytes =null;
    try {
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes());
        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        encrBytes = cipher.doFinal(value.getBytes());
        encryptedStr = new String(encrBytes);
    } catch (Exception ex) {
        ex.printStackTrace();
    }

    String strToBeEncoded = encryptedStr +"::"+initVector;
    encrBytes = strToBeEncoded.getBytes();
    //String encoded = Base64.encodeBase64String(encrBytes);
    String encoded = Base64.getEncoder().encodeToString(encrBytes);
    String urlEncoded = null;
    try {
        urlEncoded = java.net.URLEncoder.encode(encoded, CHARSET);
    } catch (UnsupportedEncodingException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return urlEncoded;
}

Decryption code:-

public String decrypt(String encrypted) {
    String decryptedStr = null;
    byte[] base64Bytes = null;
    String urlDecoded = null;
    String key = HmCommonProperty.getProperty("abcd_crypt_key");
    if(key == null || key.isEmpty()) {
        key = securityKey;
    }
    String encryptionMech = HmCommonProperty.getProperty("abcd_crypt_algo");
    if(encryptionMech == null || encryptionMech.isEmpty()) {
        encryptionMech = CRYPT_MECHANISM;
    }
    try {
        //Url and Base64 decoding
        urlDecoded = java.net.URLDecoder.decode(encrypted, CHARSET);
        //base64Bytes = Base64.decodeBase64(urlDecoded);
        base64Bytes = Base64.getDecoder().decode(urlDecoded);
        //Generating IV
        String str = new String(base64Bytes);
        String[] bodyIVArr = str.split("::");
        initVector = bodyIVArr[1];
        String bodyStr = bodyIVArr[0];

        //AES Decryption
        Cipher cipher = Cipher.getInstance(encryptionMech);
        IvParameterSpec iv = new IvParameterSpec(initVector.getBytes());

        System.out.println("initVector Length ->  "
                +iv.getIV().length);
        System.out.println("input length ->  "
                +bodyStr.getBytes().length);

        SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), "AES");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] decryptedBytes = cipher.doFinal(bodyStr.getBytes());
        decryptedStr =  new String(decryptedBytes);
    } catch (Exception ex) {
        ex.printStackTrace();
        log.error("Error occurred while decryption abcd data",ex);
    }

    return decryptedStr;
}
6
  • what is the value of CHARSET? Commented Nov 7, 2018 at 6:59
  • It's the Default charset windows-1252. I had to use it here because the java.net.URLEncoder.encode(String s, String enc) asks for it compulsorily. Commented Nov 7, 2018 at 7:09
  • 1
    Don't convert binary data (byte[]) to strings without proper encoding (e.g. base64 or hex). Commented Nov 7, 2018 at 7:17
  • @Henry Okay. But once I convert byte[] to string (say str) using the Base64 encoder, to get the actual String I want, I'll have to now use Base64 decoder on the string str, right? The problem is that the decoder method will again give me a byte[]. Commented Nov 7, 2018 at 7:35
  • It was a small challenge to get your code to run. Next time please post a minimal runnable example. Commented Nov 7, 2018 at 9:23

2 Answers 2

2

Your encrypted data is a sequence of bytes. If you need to encode it as a string, you should use base64 or a similar encoding that is intended for encoding arbitrary byte arrays. Pretending that your arbitrary byte array is a valid string-encoding is going to cause you trouble, even if you use ISO_8859_1.

Replace

encryptedStr = new String(encrBytes)

with

encryptedStr = Base64.getEncoder().encodeToString(encrBytes)

and replace

bodyStr.getBytes()

with

Base64.getDecoder().decode(bodyStr)

See also: How to correctly and consistely get bytes from a string for AES encryption?

Sign up to request clarification or add additional context in comments.

1 Comment

Helpful. Thank you. I initially thought using ISO_8859_1 was the fix.
0

Your error lies here:

encryptedStr = new String(encrBytes);
strToBeEncoded.getBytes();

These methods use the platform default character set, and when you convert from byte[] to String and back to byte[] the process is lossy in the general case. The only way it's not lossy is if the platform default character set is "ISO_8859_1".

I changed all of 11 such calls to:

encryptedStr = new String(encrBytes, StandardCharsets.ISO_8859_1);
strToBeEncoded.getBytes(StandardCharsets.ISO_8859_1);

(I didn't change CHARSET). The output I get now is:

initVector Length -> 16
input length -> 48
AkhilRanjanBiharabcdefghijklmnopMovedtoChennai18

Bonus warning 1: The encryption uses hardcoded "AES/CBC/NoPadding" but the decryption is dynamic (it should of course also use "AES/CBC/NoPadding").

Bonus warning 2: The chance is low but it's entirely possible that "::" appears inside the encrBytes, screwing up your str.split("::");. One solution is to search for the last occurrence of "::" and only split on that.

4 Comments

Thank you so much. Changing charset to ISO_8859_1 solved it :)
While using ISO_8859_1 works (because it is an encoding that uses 1 byte per character) it is conceptually not the correct thing to represent binary data.
ISO_8859_1 maps characters to bytes in a 1:1 fashion, in other words, by keeping the lower 8 bits unchanged (assuming the upper 8 bits are zero). No other encoding does that.
@MarkJeronimus But you should not expect all transports to safely be able to convey a 0x00 byte (and other control characters might also be problematic). That is why you should use an encoding designed for moving arbitrary byte arrays as strings. Base64 is the most common such encoding.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.