[java] 최종 블록이 제대로 채워지지 않은 경우

암호 기반 암호화 알고리즘을 구현하려고하는데이 예외가 발생합니다.

javax.crypto.BadPaddingException : 주어진 최종 블록이 제대로 채워지지 않았습니다.

무엇이 문제일까요?

내 코드는 다음과 같습니다.

public class PasswordCrypter {

    private Key key;

    public PasswordCrypter(String password)  {
        try{
            KeyGenerator generator;
            generator = KeyGenerator.getInstance("DES");
            SecureRandom sec = new SecureRandom(password.getBytes());
            generator.init(sec);
            key = generator.generateKey();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public byte[] encrypt(byte[] array) throws CrypterException {
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public byte[] decrypt(byte[] array) throws CrypterException{
        try{
            Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);

            return cipher.doFinal(array);
        } catch(Exception e ){
            e.printStackTrace();
        }
        return null;
    }
}

(JUnit 테스트)

public class PasswordCrypterTest {

    private static final byte[] MESSAGE = "Alpacas are awesome!".getBytes();
    private PasswordCrypter[] passwordCrypters;
    private byte[][] encryptedMessages;

    @Before
    public void setUp() {
        passwordCrypters = new PasswordCrypter[] {
            new PasswordCrypter("passwd"),
            new PasswordCrypter("passwd"),
            new PasswordCrypter("otherPasswd")
        };

        encryptedMessages = new byte[passwordCrypters.length][];
        for (int i = 0; i < passwordCrypters.length; i++) {
            encryptedMessages[i] = passwordCrypters[i].encrypt(MESSAGE);
        }
    }

    @Test
    public void testEncrypt() {
        for (byte[] encryptedMessage : encryptedMessages) {
            assertFalse(Arrays.equals(MESSAGE, encryptedMessage));
        }

        assertFalse(Arrays.equals(encryptedMessages[0], encryptedMessages[2]));
        assertFalse(Arrays.equals(encryptedMessages[1], encryptedMessages[2]));
    }

    @Test
    public void testDecrypt() {
        for (int i = 0; i < passwordCrypters.length; i++) {
            assertArrayEquals(MESSAGE, passwordCrypters[i].decrypt(encryptedMessages[i]));
        }

        assertArrayEquals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[1]));
        assertArrayEquals(MESSAGE, passwordCrypters[1].decrypt(encryptedMessages[0]));

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[0].decrypt(encryptedMessages[2])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }

        try {
            assertFalse(Arrays.equals(MESSAGE, passwordCrypters[2].decrypt(encryptedMessages[1])));
        } catch (CrypterException e) {
            // Anything goes as long as the above statement is not true.
        }
    }
}



답변

잘못된 키로 PKCS5 패딩 된 데이터를 복호화 한 다음 패딩을 해제하면 (Cipher 클래스에서 자동으로 수행됨) BadPaddingException이 발생할 가능성이 큽니다 (약 255/256 미만, 약 99.61 %) ), 패딩은 패딩을 해제하는 동안 유효성이 검사되는 특수 구조를 가지고 있고 매우 적은 수의 키가 유효한 패딩을 생성하기 때문입니다.

따라서이 예외가 발생하면이를 포착하여 “잘못된 키”로 취급하십시오.

이는 잘못된 비밀번호를 제공 한 경우 키 저장소에서 키를 가져 오는 데 사용되거나 키 생성 기능을 사용하여 키로 변환되는 경우에도 발생할 수 있습니다.

물론 전송 중에 데이터가 손상된 경우에도 잘못된 패딩이 발생할 수 있습니다.

즉, 귀하의 계획에 대한 몇 가지 보안주의 사항이 있습니다.

  • 암호 기반 암호화의 경우 KeyGenerator와 함께 SecureRandom을 사용하는 대신 SecretKeyFactory 및 PBEKeySpec을 사용해야합니다. 그 이유는 SecureRandom이 각 Java 구현에서 다른 알고리즘이되어 다른 키를 제공 할 수 있기 때문입니다. SecretKeyFactory는 정의 된 방식 (그리고 올바른 알고리즘을 선택하면 안전하다고 간주되는 방식)으로 키 파생을 수행합니다.

  • ECB 모드를 사용하지 마십시오. 각 블록을 독립적으로 암호화하므로 동일한 일반 텍스트 블록도 항상 동일한 암호문 블록을 제공합니다.

    CBC (Cipher block chaining) 또는 CTR (Counter)과 같은 보안 작동 모드를 사용하는 것이 좋습니다 . 또는 GCM (Galois-Counter 모드) 또는 CCM (CBC-MAC이있는 카운터)과 같은 인증도 포함하는 모드를 사용하십시오. 다음 사항을 참조하십시오.

  • 일반적으로 기밀성뿐만 아니라 메시지가 변조되지 않도록하는 인증도 원합니다. (이는 또한 암호에 대한 선택된 암호문 공격을 방지합니다. 즉, 기밀 유지에 도움이됩니다.) 따라서 메시지에 MAC (메시지 인증 코드)를 추가하거나 인증을 포함하는 암호 모드를 사용하십시오 (이전 요점 참조).

  • DES의 유효 키 크기는 56 비트에 불과합니다. 이 키 공간은 매우 작으며 전담 공격자가 몇 시간 내에 무차별 대입 할 수 있습니다. 비밀번호로 키를 생성하면 더 빨라집니다. 또한 DES의 블록 크기는 64 비트에 불과하므로 연결 모드에서 더 많은 약점이 추가됩니다. 대신 블록 크기가 128 비트이고 키 크기가 128 비트 (표준 변형의 경우) 인 AES와 같은 최신 알고리즘을 사용하십시오.


답변

사용중인 암호화 알고리즘에 따라 바이트 배열의 길이가 블록 크기의 배수가되도록 바이트 배열을 암호화하기 전에 끝에 패딩 바이트를 추가해야 할 수 있습니다.

특히 귀하의 경우 선택한 패딩 스키마는 PKCS5이며 여기에 설명되어 있습니다.
http://www.rsa.com/products/bsafe/documentation/cryptoj35html/doc/dev_guide/group_ CJ _SYM__PAD.html

(암호화하려고 할 때 문제가 있다고 가정합니다)

Cipher 오브젝트를 인스턴스화 할 때 패딩 스키마를 선택할 수 있습니다. 지원되는 값은 사용중인 보안 공급자에 따라 다릅니다.

그런데 대칭 암호화 메커니즘을 사용하여 암호를 암호화 하시겠습니까? 편도 해시가 더 좋지 않을까요? 정말로 암호를 해독 할 수 있어야하는 경우 DES는 매우 약한 솔루션입니다. 대칭 알고리즘을 유지해야하는 경우 AES와 같은 더 강력한 것을 사용하는 데 관심이있을 수 있습니다.


답변

이 문제는 JRE 구현에 대한 간단한 다른 플랫폼의 운영 체제로 인해 발생했습니다.

new SecureRandom(key.getBytes())

Windows에서는 동일한 값을 가져 오지만 Linux에서는 다릅니다. 따라서 Linux에서는 다음으로 변경해야합니다.

SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
secureRandom.setSeed(key.getBytes());
kgen.init(128, secureRandom);

“SHA1PRNG”는 사용되는 알고리즘입니다. 여기 에서 알고리즘에 대한 자세한 정보를 참조 할 수 있습니다 .


답변

서명 키에 잘못된 암호를 입력 한 경우에도 문제가 될 수 있습니다.


답변