首页 文章

java.security.InvalidKeyException:解密期间密钥大小错误

提问于
浏览
3

在解密期间,我得到"Wrong key size"或"Given final block not properly padded"的混合,取决于我正在运行的操作系统 .

在Win7上,使用IBMJCE或SUNJCE(两者都是Java8),解密在25%的时间内失败:

javax.crypto.BadPaddingException:在com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)的com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)中没有正确填充最终块 . )at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:294)at javax.crypto.Cipher.doFinal(Cipher.java:2087)

在mac上,使用SUNJCE,解密在100%的时间内失败:

java.security.InvalidKeyException:com.sun.crypto.provider.DESedeCrypt.init(DESedeCrypt.java:69)的密钥大小错误,位于com的com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91) .sun.crypto.provider.CipherCore.init(CipherCore.java:469)at com.sun.crypto.provider.DESedeCipher.engineInit(DESedeCipher.java:197)at javax.crypto.Cipher.implInit(Cipher.java:791) )在javax.crypto.Cipher.chooseProvider(Cipher.java:849)javax.crypto.Cipher.init(Cipher.java:1348)

使用DESEde,我相信密钥大小必须是24,我可以看到在Windows上,解密后,它总是24字节,而在mac上,它永远不会是24字节 .

这是起点 . 在decryptWithSymmetricKey期间始终抛出异常 . 注意,我对大部分代码(特定于DESede)进行了短循环,无法进一步缩小代码(非常新的安全空间) .

public static void runtest() throws Exception {
    String symmPad = "DESede/CBC/PKCS5Padding";
    String asymmPad = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
    String pubKeyFp = "somekey";
    String stringToEncrypt = "abcdefg";

    KeyGenerator kgen = KeyGenerator.getInstance(DESEDE);
    kgen.init(112);
    SecretKey secKey = kgen.generateKey();

    String encryptedKey = encryptSymmetricKey(secKey, asymmPad);
    String encryptedData = encryptDataWithSymmetricKey(stringToEncrypt, secKey, symmPad);

    String decryptedKey = decryptWithPrivateKey(encryptedKey, pubKeyFp, asymmPad);
    String decryptedData = decryptWithSymmetricKey(encryptedData, decryptedKey, symmPad);
}

这里我们加密对称密钥,两个环境中的密钥长度为24

private static String encryptSymmetricKey(SecretKey secKey, String asymmPadding) throws Exception {
    KeyPair keyPair = getKeyPair("self4");
    Cipher cipher = Cipher.getInstance(asymmPadding);

    OAEPParameterSpec ospec = new OAEPParameterSpec(SHA256, MGF1, MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
    cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic(), ospec);

    String secKeyEncoded = new String(secKey.getEncoded());
    byte[] encrypted = cipher.doFinal(secKeyEncoded.getBytes());

    char[] encoded = Hex.encodeHex(encrypted);
    return new String(encoded);
}

在这里,我们使用对称密钥加密我们的字符串

private static String encryptDataWithSymmetricKey(String data, SecretKey secretKey, String symmPadding) throws Exception {
    Cipher cipher = Cipher.getInstance(symmPadding);
    IvParameterSpec iv = new IvParameterSpec(new byte[8]);
    cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);

    byte[] encrypted = cipher.doFinal(data.getBytes());
    char[] encoded = Hex.encodeHex(encrypted);
    return new String(encoded);
}

解密和解码对称密钥是我第一次看到mac上的可变长度密钥 .

public String decryptWithPrivateKey(String encryptedData, String pubKeyFp, String asymmPadding) throws Exception {
    loadKeystores();
    String alias = fingerPrintAliasMap.get(pubKeyFp);

    KeyPair keyPair = getKeyPair(alias);
    Cipher cipher = Cipher.getInstance(asymmPadding);

    OAEPParameterSpec oParamSpec = new OAEPParameterSpec(SHA256, MGF1, MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
    cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate(), oParamSpec);

    byte[] decoded = Hex.decodeHex(encryptedData.toCharArray());
    byte[] decrypted = cipher.doFinal(decoded);
    System.out.println("decoded and decrypted key length: " + decrypted.length); // 24 on windows, random on mac

    return new String(Hex.encodeHex(decrypted));
}

失败发生在这里 - 在Windows上,它在cipher.doFinal 25%的时间内失败,在mac上,它在100%的时间在cipher.init失败 .

public String decryptWithSymmetricKey(String encryptedHexData, String symmKey, String symmPadding) throws Exception {

    byte[] key = Hex.decodeHex(symmKey.toCharArray());
    SecretKey skeySpec = new SecretKeySpec(key, DESEDE);
    IvParameterSpec iv = new IvParameterSpec(new byte[8]);

    Cipher cipher = Cipher.getInstance(symmPadding);
    cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); // mac: Wrong key size

    byte[] decoded = Hex.decodeHex(encryptedHexData.toCharArray());
    byte[] deciphered = cipher.doFinal(decoded); // windows: Given final block not properly padded

    return new String(deciphered);
}

我假设如果我在mac上解决这个问题,它也应该在Windows上解决它 .

1 回答

  • 5

    问题是 encryptDataWithSymmetricKey 方法中的以下两行:

    String secKeyEncoded = new String(secKey.getEncoded());
    byte[] encrypted = cipher.doFinal(secKeyEncoded.getBytes());
    

    由于您随机生成了DES密钥,因此很可能包含不可打印的字符 . 通过调用 new String(bytes) ,您将默默地删除那些打破密钥的不可打印的字符 . 改为使用:

    byte[] encrypted = cipher.doFinal(secKey.getEncoded());
    

    其他考虑因素:

    此外,如果您生成112位,我不确定您的密钥大小是否正确 . 我认为Java期望密钥大小包括奇偶校验位,该安全级别为128 . 您可能应该使用192位,这样可以提供更好的安全级别(DESede只有112位,192位密钥= 168位而不计算奇偶校验位) .

    你不应该使用DESede . 即使AES-128也能提供更好的安全性 .

    请不要使用零字节的静态IV . 使用强大的随机源(如 SecureRandom )为每个加密生成IV . 它不必是秘密的,因此您可以简单地将其添加到密文中,并且在解密之前不要忘记将其切掉 .

    您没有验证对称密文 . 您需要使用加密 - 然后MAC方法与强大的MAC(如HMAC-SHA256)或使用经过身份验证的操作模式(如GCM或EAX) .

相关问题