0

I am trying to decrypt a String with a known key in Java using standard Cipher API.

The encrypted String comes from a Web Service using the standard CommonCrypto Library which responds with some statistics as encrypted strings at regular intervals.

The specs are AES/CBC/PKCS7Padding with KeySize = 32 Bytes and BlockSize = 16 Bytes, and Encoding UTF-8 (raw) & Base64. I intend to write a Java client that can request these statistics, decrypt them and store them for later analyses.

Question 1. Does the CommonCrypto automatically pad keys with extra chars if the key is short? For instance less than 16 Bytes or 32 Bytes.

Question 2. What encoding measures should I take to ensure an identical encryption/decryption on both ends?

Example Strings and Key

String message = "mQp9sp8ri1E0V1Xfso1d5g==Mrf3wtaqUjASlZmUO+BI8MrWsrZSC0MxxMocswfYnqSn/VKB9luv6E8887eCxpLNNAOMB0YXv6OS7rFDFdlvC53pCHo3cVZiLJFqgWN/eNiC9p4RMxyFCcOzWrwKzT5P8sy55DwE25DNJkvMthSaxK5zcP1OdLgBiZFOSxYRsX4rBk7VP7p5xr2uTGjRL+jmGgB9u3TmeCNCr8NxGLNt6g==";
String userKey = "123456789";

private static String decrypt (String message, String userKey) throws UnsupportedEncodingException,
            NoSuchPaddingException,
            NoSuchAlgorithmException,
            InvalidKeyException,
            ShortBufferException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, NoSuchProviderException {
        Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
        if (message.length() >= 48) {

        ivFromEncryptedString = message.substring(0, Math.min(message.length(), 24));
        messageFromEncryptedString = message.substring(24, message.length());

        System.out.println(ivFromEncryptedString);
        System.out.println(messageFromEncryptedString);

        byte[] data = decodeBase64(messageFromEncryptedString);
        byte[] ivData = decodeBase64(ivFromEncryptedString);

        paddedKey = padShortKeys(userKey);

        byte[] keyBytes = paddedKey.getBytes(CHARSET);

        MessageDigest sha = MessageDigest.getInstance("SHA-256"); //key
        keyBytes = sha.digest(keyBytes);

        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(ivData);
        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);

            byte [] encrypted = new byte[cipher.getOutputSize(data.length)];
            int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
            ctLength += cipher.doFinal(encrypted, ctLength);
        } catch (Exception e) {
            System.out.println(e);
        } finally {
            return encrypted;
        }
    }
    return null;
    }
private static String encodeBase64(byte [] in){
    return Base64.getEncoder().encodeToString(in);
}

private static byte[] decodeBase64(String str) throws UnsupportedEncodingException {
    return DatatypeConverter.parseBase64Binary(str);
}

Also with the current code status I am getting placehoder characters instead of the desired result.

Thanks in advance folks. :)

  • You split the message into IV and encrypted data. For the IV, you take 24 character of a Base 64 encoded string, i.e. 18 raw bytes. Is your IV really 18 bytes long? – Codo Jun 30 '18 at 20:48
  • 1. Why are you padding the key when it will be passed into MD5, that makes no sense. 2. MD5 produces a 16-byte output and you are using AES with a 32-byte key, again that makes no sense. 3. MD5 is not a secure key derivation function, PBKDF2 is secure. – zaph Jun 30 '18 at 22:29
  • @Codo The 24 characters are Base64 encoded and decode to 16-bytes, the last two characters "==" are Base64 padding. See [Base64 Output Padding](https://en.wikipedia.org/wiki/Base64#Output_padding). – zaph Jun 30 '18 at 22:32
  • In addition to everything zaph said, you are using `Cipher.update()` and `Cipher.doFinal()` incorrectly. Please re-read the Javadocs for those methods **carefully**. I suspect all you want is a single call to `doFinal()`, ie. `byte [] out = cipher.doFinal(data)`. – President James K. Polk Jul 01 '18 at 12:27
  • @Codo IV is supposed to be 16 Bytes. – javanewbie Jul 01 '18 at 22:37
  • @zaph Yes, I am definitely not new to writing. I will try my best to edit it in a readable way. – javanewbie Jul 01 '18 at 22:38
  • @zaph Sir, I have tried to rewrite it and updated the code as well as per your suggestions. However still the output is wierd placeholder characters instead of desired result. If you could look into it again I would be very thankful. Anyways thanks for your suggestions. – javanewbie Jul 02 '18 at 05:53

1 Answers1

1

CommonCrypto is unclear, which implementation are you using? Apple, Apache, Java Class Cipher or another, please supply a link to it.

  1. Never assume an encryption will pad the key or IV, they should always be provided in the exact length, there is no standard for such padding. If they need padding (they shouldn't) do it yourself.

  2. Typically if encrypted data needs to be expressed as a character string Base64 encoding is used.

As James states, for one-shot encryption just use doFinal(ByteBuffer input, ByteBuffer output) which encrypts or decrypts data in a single-part operation.

Note: A 9 digit key only has about 33-bits of security which is not close to sufficient. Simple using a hash function is insufficient for deriving an encryption key from a password, instead PBKDF2 or Argon2 should be used.

zaph
  • 111,848
  • 21
  • 189
  • 228
  • Apple, sir. The encrypted string comes from an Apple device along with the IV in the front which is `16 Bytes` and the `Key` is being padded there with the `Char 1`. It is a standard encryption but there is a method exposed which says padKeys and the padding limit is a magic number `25`. That is what made me question if keys are padded automatically via `CommonCrypto`. Because the key length is `kCCKeySizeAES256` so `25 Bytes` must be converted to `32 Bytes`. – javanewbie Jul 02 '18 at 14:53
  • Thank you sir. I cordially appreciate your valuable time on this. – javanewbie Jul 02 '18 at 18:14
  • Update: On testing Apple Common Crypto does **not** pad a short key with nulls. – zaph Jul 02 '18 at 19:28