Java Decryption - Input length must be multiple of

2019-04-17 06:43发布

问题:

I have a project for a security course but i'm having an issue.

basically , i'm trying to encrypt then decrypt a password but i'm getting this error for the decryption .

"Input length must be multiple of 8 when decrypting with padded cipher"

Am i doing this the right way . I was following an article from 2012 . Is it still secure ?

Also i tried replacing the algorithm but nothing seems to work :

"AES" , "RSA/ECB/PKCS1Padding" , "PBEWithHmacSHA256AndDESede"..and many more

i get :

Exception in thread "main" java.security.NoSuchAlgorithmException: RSA/ECB/PKCS1Padding SecretKeyFactory not


import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;

import java.util.Base64;

public class ProtectedConfigFile {

    private static final char[] PASSWORD = "ytfchchchgcv".toCharArray();
    private static final byte[] SALT = {
        (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
        (byte) 0xde, (byte) 0x33, (byte) 0x10, (byte) 0x12,
    };

  public static void main(String[] args) throws Exception {
        String originalPassword = "secret";
        System.out.println("Original password: " + originalPassword);
        String encryptedPassword = encrypt(originalPassword);
        System.out.println("Encrypted password: " + encryptedPassword);
        String decryptedPassword = decrypt(encryptedPassword);
        System.out.println("Decrypted password: " + decryptedPassword);
    }

    private static String encrypt(String property) throws GeneralSecurityException, UnsupportedEncodingException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
        pbeCipher.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
        return Base64.getEncoder().encodeToString(pbeCipher.doFinal(property.getBytes("UTF-8")));
    }

    private static String decrypt(String property) throws GeneralSecurityException, IOException {
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
        SecretKey key = keyFactory.generateSecret(new PBEKeySpec(PASSWORD));
        Cipher pbeCipher = Cipher.getInstance("PBEWithMD5AndDES");
        pbeCipher.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(SALT, 20));
        byte[] decode = Base64.getDecoder().decode(pbeCipher.doFinal(property.getBytes("UTF-8")));
        return decode.toString();
    }

}

This returns :

Original password: secret
Encrypted password: eG+qiRan1Cw=
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:913)
    at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
    at com.sun.crypto.provider.PBES1Core.doFinal(PBES1Core.java:416)
    at com.sun.crypto.provider.PBEWithMD5AndDESCipher.engineDoFinal(PBEWithMD5AndDESCipher.java:316)
    at javax.crypto.Cipher.doFinal(Cipher.java:2165)
    at ProtectedConfigFile.decrypt(ProtectedConfigFile.java:43)
    at ProtectedConfigFile.main(ProtectedConfigFile.java:26)

回答1:

You've put the base64 encoding of the ciphertext in encryptedPassword. You need to first base64-decode that string and then decrypt the actual ciphertext. And since your plaintext was in UTF-8, you should interpret the decrypted plaintext as UTF-8.

return new String( pbeCipher.doFinal(Base64.getDecoder().decode(property)), "UTF-8");

And no DES is not secure, and in fact has been in brute-force range for at least a decade depending on attacker capabilities. It has been deprecated since about 1999, in favor first of "triple-DES" (formally TDEA) and then AES, and was officially withdrawn about 2005 as I recall. See the second paragraph of https://en.wikipedia.org/wiki/Data_Encryption_Standard . Anything written in 2012 that advised using DES was incompetent.

MD5 is broken for collision, and thus for signature (or at least certificate signature). As far as I know this doesn't translate into an attack on PBKDF, even the older and deprecated PBKDF1 used for PBEwithMD5andDES, but it has led many to prohibit all uses of MD5.

(updated) The builtin providers in all recent Oracle Java include Cipher PBEwithSHA1andDESede which is much better, and SecretKeyFactory PBKDF2WithHmacSHA1 from which you can construct any (reasonable) PBES2 cipher. (That former is actually PKCS#12 pbeWithSHAAnd3-keyTripleDES-CBC and formally known as 1.2.840.113549.1.12.1.3 .) Java 8 (only) adds several good PBES2 prepackaged as Cipher PBEWithHmacSHA{1,224,256,384,512}AndAES_{128,256}. Any of those is fine, although with SHA1 you might have to explain to uninformed people that SHA1 is now considered at risk for collision and prohibited for certificates but still fine for KDF and PBE, which you could avoid by just going to any of the SHA2, probably SHA256 as the most popular. Plus you should definitely use more iterations than 20; even in 1998 RFC2898 recommended at least 1000, and today at least 10s of thousands and usually a million or more is popular.