Search code examples
javaandroidencryptioninputstreamandroid-version

Problem with decryption , cipher in android AES/CTR/NoPadding


When i use this code on android Marshmallow(Android 6.0.1) ,the decryption is Ok but when i run on device with android Oreo(Android 8) the decryption value is not the same and data is not Correct.

private void decrypt(Cipher cipher, Uri uri) throws Exception {
    long a = 113845229;
    InputStream inputStream = getContentResolver().openInputStream(uri);
    CipherInputStream cipherInputStream = new CipherInputStream(inputStream, cipher);
    cipherInputStream.skip(a);
    byte[] buffer = new byte[8];
    cipherInputStream.read(buffer);
}

// create cipher
private Cipher createCipher(byte[] iv, byte[] salt, String password) throws Exception {
    IvParameterSpec mIvParameterSpec = new IvParameterSpec(iv);
    SecretKeySpec mSecretKeySpec = generate(password, salt);
    Cipher mCipher = Cipher.getInstance("AES/CTR/NoPadding");
    mCipher.init(Cipher.DECRYPT_MODE, mSecretKeySpec, mIvParameterSpec);
    return mCipher;
}
// generate key
private SecretKeySpec generate(String password, byte[] salt) throws Exception {
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    md.update(salt);
    byte[] key = md.digest(password.getBytes(StandardCharsets.UTF_8));
    return new SecretKeySpec(key, "AES");
}

buffer data is ok in android 6 but in android 8 the data is not correct.


Solution

  • I came to the conclusion after research .you should implement InputStream with specific Cipher.

    private static final int AES_BLOCK_SIZE = 16;
    private InputStream mUpstream;
    private Cipher mCipher;
    private SecretKeySpec mSecretKeySpec;
    private IvParameterSpec mIvParameterSpec;
    
    public StreamingCipherInputStream(InputStream inputStream, Cipher cipher, 
        SecretKeySpec secretKeySpec, IvParameterSpec ivParameterSpec) {
        super(inputStream, cipher);
        mUpstream = inputStream;
        mCipher = cipher;
        mSecretKeySpec = secretKeySpec;
        mIvParameterSpec = ivParameterSpec; }
    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return super.read(b, off, len);  }
    public long forceSkip(long bytesToSkip) throws IOException {
        long skipped = mUpstream.skip(bytesToSkip);
        try {
            int skip = (int) (bytesToSkip % AES_BLOCK_SIZE);
            long blockOffset = bytesToSkip - skip;
            long numberOfBlocks = blockOffset / AES_BLOCK_SIZE;
    
            BigInteger ivForOffsetAsBigInteger = new BigInteger(1, 
            mIvParameterSpec.getIV()).add(BigInteger.valueOf(numberOfBlocks));
            byte[] ivForOffsetByteArray = ivForOffsetAsBigInteger.toByteArray();
            IvParameterSpec computedIvParameterSpecForOffset;
            if (ivForOffsetByteArray.length < AES_BLOCK_SIZE) {
                byte[] resizedIvForOffsetByteArray = new byte[AES_BLOCK_SIZE];
                System.arraycopy(ivForOffsetByteArray, 0, resizedIvForOffsetByteArray, 
                AES_BLOCK_SIZE - ivForOffsetByteArray.length, ivForOffsetByteArray.length);
                computedIvParameterSpecForOffset = new IvParameterSpec(resizedIvForOffsetByteArray);
            } else {
                computedIvParameterSpecForOffset = new IvParameterSpec(ivForOffsetByteArray, ivForOffsetByteArray.length - AES_BLOCK_SIZE, AES_BLOCK_SIZE);
            }
            mCipher.init(Cipher.ENCRYPT_MODE, mSecretKeySpec, computedIvParameterSpecForOffset);
            byte[] skipBuffer = new byte[skip];
            mCipher.update(skipBuffer, 0, skip, skipBuffer);
            Arrays.fill(skipBuffer, (byte) 0);
        } catch (Exception e) {
            return 0;
        }
        return skipped;
    }
    @Override
    public int available() throws IOException {
        return mUpstream.available();}