Java에서 AES / CBC / PKCS5Padding을 사용한 암호화 후 파일 크기는 얼마입니까?
소켓을 사용하여 파일을 보낼 때 암호화 및 암호 해독을 사용하려고했습니다. 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));
}
답변
부적절한 코드
PresidentJamesK.Polk가 언급 했듯이 암호화를 완료하기 위해 를 CipherOutputStream
호출하는 .close()
함수를 호출하여 발신자 측에서을 닫아야합니다 doFinal()
.
패딩 크기
Java가 PKCS5Padding
실제로는 PKCS#7 Padding
. PKCS#5 Padding
8 옥텟, 즉 DES와 같은 64 비트 블록 크기의 블록 암호에 대해 정의됩니다.
PKCS # 7 표준은 rfc2315 (10.3 참고 2)에 있습니다.
그러한 알고리즘의 경우, 방법은 값이 k-(l mod k) 인 k-(l mod k) 옥텟으로 후미의 입력을 채우는 것입니다. 여기서 l은 입력의 길이입니다.
옥텟은 1 바이트이고는 k
블록 크기 (바이트)이며 16
AES 용입니다.
우리는 계산해야합니다 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를 선호 할 수 있습니다 .
이것은 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 의 대답, 나는 문제가 있다는 것을 깨달았다 cipherInputStream
과 cipherOutputStream
가까운 제대로하지 않았다. 사실, 나는 닫은 inputStream
및 outputStream
다음 암호 스트림. 그래서 먼저 암호 스트림을 닫은 다음 다른 스트림을 닫았으며 이제 암호화가 제대로 작동합니다.