代码之家  ›  专栏  ›  技术社区  ›  mehere

OpenSSL加密密钥和IV与Java程序生成的不同

  •  2
  • mehere  · 技术社区  · 7 年前

    我正在修改一个Java程序来解密由OpenSSL加密的密码。现在我得到以下例外

    Exception in thread "main" javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.
        at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:991)
        at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:847)
        at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
        at javax.crypto.Cipher.doFinal(Cipher.java:2222)
        at test.TestAesDecrypt.main(TestAesDecrypt.java:107)
    

    通过各个环节,了解到这是由于按键产生的差异。OpenSSL和程序中使用的密钥和iv是不同的。它们是由OpenSSL和program在内部生成的。

    如果我将OpenSSL密钥和iv硬编码到程序中,解密就可以工作了。

    要加密的密码是helloworld!

    原始程序来自 here .

    如何解决相同的问题?

    C:\WINDOWS\system32>C:\OpenSSL-Win64\bin\openssl.exe aes-256-cbc -salt -in D:\Misc\tryouts\encryption3\password.txt -out D:\Misc\tryouts\encryption3\enpassfile.encr -pass file:D:\Misc\tryouts\encryption3\passkeyfile.txt -p
    salt=E68886B9E9C2ACD8
    key=5F9A0BAF409AF8F9B9FECA9008508A91CEEE86B3A334EC6E00D08A7A8B8372C5
    iv =FF3EC566671077A5CC7F0695F8CC590B
    
    C:\WINDOWS\system32>C:\OpenSSL-Win64\bin\openssl.exe aes-256-cbc -d -salt -in D:\Misc\tryouts\encryption3\enpassfile.encr -out D:\Misc\tryouts\encryption3\decpassfile.dcr -pass file:D:\Misc\tryouts\encryption3\passkeyfile.txt -p
    salt=E68886B9E9C2ACD8
    key=5F9A0BAF409AF8F9B9FECA9008508A91CEEE86B3A334EC6E00D08A7A8B8372C5
    iv =FF3EC566671077A5CC7F0695F8CC590B
    
    Values from Java Program:
    Values from Java Program:
    Encrypted cipher: 53616C7465645F5FE68886B9E9C2ACD84CB17D3CA8FEFC54E189766B97E815BD
    Encrypted cipher via Apache Commons Hex: 53616c7465645f5fe68886b9e9c2acd84cb17d3ca8fefc54e189766b97e815bd
    Pass Key: 54494B706173736B6579303031303032
    Pass Key: TIKpasskey001002
    
    keyValue: 6F16A5F5DA36A6A479CEBFAB6A8258D490A0A3994D204AC1AFD338AF7B4D4306
    keyValue via Apache Commons Hex: 6f16a5f5da36a6a479cebfab6a8258d490a0a3994d204ac1afd338af7b4d4306
    IV: E19C6128D0851D09F7E01F34061C88E3
    IV via Apache Commons Hex: e19c6128d0851d09f7e01f34061c88e3
    

    这个项目是

    import java.io.File;
    import java.io.FileInputStream;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.security.MessageDigest;
    import java.util.Arrays;
    
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import org.apache.commons.codec.binary.Hex;
    import org.apache.commons.io.IOUtils;
    
    public class TestAesDecrypt {
    
        private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
    
        public static String bytesToHex(byte[] bytes) {
            char[] hexChars = new char[bytes.length * 2];
            for (int j = 0; j < bytes.length; j++) {
                int v = bytes[j] & 0xFF;
                hexChars[j * 2] = hexArray[v >>> 4];
                hexChars[j * 2 + 1] = hexArray[v & 0x0F];
            }
            return new String(hexChars);
        }
    
        public static byte[] getBytes(String inputFile) {
    
            String outputFile = inputFile;
            File infile = new File(outputFile);
    
            byte[] readBytes = null;
            try {
    
                readBytes = IOUtils.toByteArray(new FileInputStream(infile));
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return readBytes;
        }
    
        public static void main(final String[] args) throws Exception {
    
            final byte[] pass = getBytes("D:\\\\Misc\\\\tryouts\\\\encryption3\\\\passkeyfile.txt");
            final byte[] magic = "Salted__".getBytes(StandardCharsets.US_ASCII);
            final String inFile = "D:\\Misc\\tryouts\\encryption3\\enpassfile.encr";
    
            final byte[] inBytes = Files.readAllBytes(Paths.get(inFile));// decoder.decode(source);
    
            System.out.println("Encrypted cipher: " + bytesToHex(inBytes));
            System.out.println("Encrypted cipher via Apache Commons Hex: " + Hex.encodeHexString(inBytes));
    
            System.out.println("Pass Key: " + bytesToHex(pass));
            System.out.println("Pass Key: " + new String(pass));
    
            final byte[] shouldBeMagic = Arrays.copyOfRange(inBytes, 0, magic.length);
            if (!Arrays.equals(shouldBeMagic, magic)) {
                System.out.println("Bad magic number");
                return;
            }
    
            final byte[] salt = Arrays.copyOfRange(inBytes, magic.length, magic.length + 8);
    
            final byte[] passAndSalt = concat(pass, salt);
    
            byte[] hash = new byte[0];
            byte[] keyAndIv = new byte[0];
            for (int i = 0; i < 3; i++) {
                final byte[] data = concat(hash, passAndSalt);
                final MessageDigest md = MessageDigest.getInstance("MD5");
                hash = md.digest(data);
                keyAndIv = concat(keyAndIv, hash);
            }
    
            final byte[] keyValue = Arrays.copyOfRange(keyAndIv, 0, 32);
            final byte[] iv = Arrays.copyOfRange(keyAndIv, 32, 48);
    
            /*
             * Key and IV from OpenSSL command -p 
             * final byte[] keyValue =
             * javax.xml.bind.DatatypeConverter.parseHexBinary(
             * "5F9A0BAF409AF8F9B9FECA9008508A91CEEE86B3A334EC6E00D08A7A8B8372C5"); final
             * byte[] iv = javax.xml.bind.DatatypeConverter.parseHexBinary(
             * "FF3EC566671077A5CC7F0695F8CC590B");
             */
    
            final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            final SecretKeySpec key = new SecretKeySpec(keyValue, "AES");
    
            System.out.println("IV: " + bytesToHex(iv));
            System.out.println("IV via Apache Commons Hex: " + Hex.encodeHexString(iv));
    
            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
            /**** EXCEPTION OCCURS HERE START *****/
            final byte[] clear = cipher.doFinal(inBytes, 16, inBytes.length - 16);
            /**** EXCEPTION OCCURS HERE STOP *****/
            final String clearText = new String(clear, StandardCharsets.ISO_8859_1);
            System.out.println(clearText);
        }
    
        private static byte[] concat(final byte[] a, final byte[] b) {
            final byte[] c = new byte[a.length + b.length];
            System.arraycopy(a, 0, c, 0, a.length);
            System.arraycopy(b, 0, c, a.length, b.length);
            return c;
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   dave_thompson_085    7 年前

    除了根据注释从密码值中删除CRLF之外 reinier :

    OpenSSL 1.1.0 或更高,哪个 在中默认使用 enc 至SHA256 man enc (可能需要特殊章节,如 1ssl )在一个单轴系统上,而你的显然不是 on the web 根据历史,也许 Password to key function compatible with OpenSSL commands?

    仅供参考,您不需要复制阵列切片;二者都 SecretKeySpec IvParameterSpec 具有使用字节数组的一部分而不是整个数组的构造函数重载。AES-256密钥是32字节(至少在Java中是这样),这是您编码的,尽管您的注释不同。