Java에서 AES / CBC / PKCS5Padding을 사용한 암호화 후 파일 크기는 얼마입니까?

Nov 15 2020

소켓을 사용하여 파일을 보낼 때 암호화 및 암호 해독을 사용하려고했습니다. AES/CBC/PKCS5Padding알고리즘을 사용 하고 먼저 IV스트림에 파일을 씁니다 .

문제는 파일이 수신 될 때 루프가 종료되지 않는다는 것입니다.

파일 크기 때문이라고 생각하고 암호화 된 파일이 원본 파일보다 작은 것 같지만 원본 파일의 크기를 수신자에게 알려줍니다. 이 가설이 맞다면 암호화 된 파일의 크기를 계산하는 방법이 있습니까?

파일 발신자

            SecretKey keySpec = new SecretKeySpec(key, "AES");
            byte[] iv = AES.randomNonce(16);
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivParameterSpec);

            outputStream = socket.getOutputStream();
            outputStream.write(iv);
            outputStream.flush();
            inputStream = context.getContentResolver().openInputStream(message.getUri());
            cipherOutputStream = new CipherOutputStream(outputStream, cipher);

            long size = message.getSize();
            long written = 0;
            byte[] buffer = new byte[8192];
            int count;
            int percent = 0;
            while (!isStopped && (count = inputStream.read(buffer)) > 0) {
                cipherOutputStream.write(buffer, 0, count);
                written += count;
                int p = (int) (((float) written / (float) size) * 100);
                if (percent != p) {
                    percent = p;
                    if (onProgressListener != null) {
                        onProgressListener.onProgress(percent);
                    }
                }
            }
            cipherOutputStream.flush();
            if (onProgressListener != null) {
                onProgressListener.onEnd(null);
            }

파일 수신기

            inputStream = socket.getInputStream();
            fileOutputStream = new FileOutputStream(file);
            byte[] iv = new byte[16];
            inputStream.read(iv);

            SecretKey keySpec = new SecretKeySpec(key, "AES");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);

            cipherInputStream = new CipherInputStream(inputStream, cipher);

            long size = message.getSize();
            long read = size;
            byte[] buffer = new byte[8192];
            int count;
            int percent = 0;
            while (!isStopped && read > 0 && (count = cipherInputStream.read(buffer, 0, (int) Math.min(buffer.length, read))) != -1) {
                fileOutputStream.write(buffer, 0, count);
                read -= count;
                int p = (int) (((float) read / (float) size) * 100);
                if (percent != p) {
                    percent = p;
                    if (onProgressListener != null) {
                        onProgressListener.onProgress(100 - percent);
                    }
                }
            }
            if (onProgressListener != null) {
                onProgressListener.onEnd(Uri.fromFile(file));
            }

답변

4 kelalaka Nov 15 2020 at 02:59

부적절한 코드

PresidentJamesK.Polk가 언급 했듯이 암호화를 완료하기 위해 를 CipherOutputStream호출하는 .close()함수를 호출하여 발신자 측에서을 닫아야합니다 doFinal().

패딩 크기

Java가 PKCS5Padding실제로는 PKCS#7 Padding. PKCS#5 Padding8 옥텟, 즉 DES와 같은 64 비트 블록 크기의 블록 암호에 대해 정의됩니다.

PKCS # 7 표준은 rfc2315 (10.3 참고 2)에 있습니다.

그러한 알고리즘의 경우, 방법은 값이 k-(l mod k) 인 k-(l mod k) 옥텟으로 후미의 입력을 채우는 것입니다. 여기서 l은 입력의 길이입니다.

옥텟은 1 바이트이고는 k블록 크기 (바이트)이며 16AES 용입니다.

우리는 계산해야합니다 IV_length + message_size_with_padding

l암호화 할 바이트 가 있다고 가정 하면 출력 크기는 다음과 같습니다.

16 + l + 16 - (l mod 16) 따라서 패딩으로 인해 최대 16 바이트까지 확장됩니다.


참고 CBC 모드가 적용 가능한 패딩 오라클 공격에 취약합니다. CBC 모드는 기밀성 만 제공합니다. 무결성 및 인증이 필요한 경우 (필요한 경우) HMAC와 함께 CBC를 사용하거나 패딩이 전혀 필요하지 않은 AES-GCM과 같은 인증 된 암호화 모드를 사용하는 것이 좋지만 태그도 저장해야합니다. AES-GCM에 대한 nonce 재사용 문제가 두렵다면 nonce 오용 방지 체계 인 AES-GCM-SIV를 사용할 수 있습니다. AES를 사용할 의무가없는 경우 AES-GCM [1] [2] 보다 사용하기 쉬운 ChaCha20-Poly1305를 선호 할 수 있습니다 .

1 HusseinYaqoobi Dec 02 2020 at 04:05

이것은 Android 프로젝트의 문제입니다. Android Studio에서이 코드를 실행할 때 Logcat에 예외가 표시되지 않았습니다. 하지만 Eclipse를 사용하여 Windows에서이 코드를 실행하면 다음과 같은 예외가 발생합니다.

java.io.IOException: javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

따라서 기반으로 Kelalaka 의 대답, 나는 문제가 있다는 것을 깨달았다 cipherInputStreamcipherOutputStream가까운 제대로하지 않았다. 사실, 나는 닫은 inputStreamoutputStream다음 암호 스트림. 그래서 먼저 암호 스트림을 닫은 다음 다른 스트림을 닫았으며 이제 암호화가 제대로 작동합니다.