/** * Gets CryptoCipher from pool. * * @return the CipherState instance. * @throws IOException if an I/O error occurs. */ private CipherState getCipherState() throws IOException { CipherState state = cipherPool.poll(); if (state == null) { CryptoCipher cryptoCipher; try { cryptoCipher = CryptoCipherFactory.getCryptoCipher("AES/CTR/NoPadding", props); } catch (GeneralSecurityException e) { throw new IOException(e); } state = new CipherState(cryptoCipher); } return state; }
/** * Does the decryption using inBuffer as input and outBuffer as output. * * @param state the CipherState instance. * @param inByteBuffer the input buffer. * @param outByteBuffer the output buffer. * @throws IOException if an I/O error occurs. */ private void decryptBuffer(CipherState state, ByteBuffer inByteBuffer, ByteBuffer outByteBuffer) throws IOException { int inputSize = inByteBuffer.remaining(); try { int n = state.getCryptoCipher().update(inByteBuffer, outByteBuffer); if (n < inputSize) { /** * Typically code will not get here. CryptoCipher#update will * consume all input data and put result in outBuffer. * CryptoCipher#doFinal will reset the cipher context. */ state.getCryptoCipher().doFinal(inByteBuffer, outByteBuffer); state.reset(true); } } catch (ShortBufferException e) { throw new IOException(e); } catch (IllegalBlockSizeException e) { throw new IOException(e); } catch (BadPaddingException e) { throw new IOException(e); } }
/** * This method is executed immediately after decryption. Check whether * cipher should be updated and recalculate padding if needed. * * @param state the CipherState instance. * @param inByteBuffer the input buffer. * @param position the offset from the start of the stream. * @param iv the iv. * @return the padding. * @throws IOException if an I/O error occurs. */ private byte postDecryption(CipherState state, ByteBuffer inByteBuffer, long position, byte[] iv) throws IOException { byte padding = 0; if (state.isReset()) { /* * This code is generally not executed since the cipher usually * maintains cipher context (e.g. the counter) internally. However, * some implementations can't maintain context so a re-init is * necessary after each decryption call. */ resetCipher(state, position, iv); padding = getPadding(position); inByteBuffer.position(padding); } return padding; }
/** * Calculates the counter and iv, reset the cipher. * * @param state the CipherState instance. * @param position the offset from the start of the stream. * @param iv the iv. * @throws IOException if an I/O error occurs. */ private void resetCipher(CipherState state, long position, byte[] iv) throws IOException { final long counter = getCounter(position); CtrCryptoInputStream.calculateIV(getInitIV(), counter, iv); try { state.getCryptoCipher().init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv)); } catch (InvalidKeyException e) { throw new IOException(e); } catch (InvalidAlgorithmParameterException e) { throw new IOException(e); } state.reset(false); }