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

java.security.InvalidKeyException:解密期间密钥大小错误,java,encryption,cryptography,java-8,Java,Encryption,Cryptography,Java 8,在解密过程中,我得到了“错误的密钥大小”或“给定的最终块未正确填充”的混合,这取决于我运行的操作系统 在Win7上,使用IBMJCE或SUNJCE(都是Java8),解密失败的时间占25%: javax.crypto.BadPaddingException:给定的最后一个块未正确填充 位于com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811) 位于com.sun.crypto.provider.CipherCore.doFi

在解密过程中,我得到了“错误的密钥大小”或“给定的最终块未正确填充”的混合,这取决于我运行的操作系统

在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) 位于com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:294) 位于javax.crypto.Cipher.doFinal(Cipher.java:2087)

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

java.security.InvalidKeyException:密钥大小错误 位于com.sun.crypto.provider.DESedeCrypt.init(DESedeCrypt.java:69) 位于com.sun.crypto.provider.CipherBlockChaining.init(CipherBlockChaining.java:91) 位于com.sun.crypto.provider.CipherCore.init(CipherCore.java:469) 位于com.sun.crypto.provider.DESedeCipher.engineInit(DESedeCipher.java:197) 位于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字节

这是起点。在使用密钥解密期间始终引发异常。注意,我短循环了大部分代码(特定于DESede),无法进一步缩小它的范围(对于安全空间来说是非常新的)

这里我们加密对称密钥,两种环境中的密钥长度都是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上,它在cipher.init 100%的时间内失败

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上解决。

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

String secKeyEncoded = new String(secKey.getEncoded());
byte[] encrypted = cipher.doFinal(secKeyEncoded.getBytes());
由于您随机生成DES密钥,它很可能包含不可打印的字符。通过调用
newstring(bytes)
,您将自动删除那些断开密钥的不可打印字符。改用:

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

其他考虑: 另外,如果生成112位,我不确定密钥大小是否正确。我认为Java希望密钥大小包括奇偶校验位,对于该安全级别,奇偶校验位为128。您可能应该使用192位,这会提供更好的安全级别(对于带有192位密钥的DESede,只有112位=168位,不计算奇偶校验位)

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

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


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

字符是否可打印并不重要,但密钥字节是否根据平台的默认字符编码形成有效序列,这两种方法都使用。无效字节通常不会被删除,而是由一个特殊字符替换,例如,
“?”
,然而,无效字节的处理方法是未指定的,细节无论如何都无关紧要。您的答案是正确的,因为这个转换是有损的。感谢您,花了3天时间试图解决这个问题,您的cipher.doFinal()解决方案为我解决了两端的问题。关于DESede,这是一个功能需求,所以我必须使用它。使用SunJCE(我不能说是IBMJCE)KeyGenerator for DESede调用112生成一个“双键”值,其中112位随机加(无用)奇偶校验存储在24字节中,如K1、K2、K1。如果调用168,则在24字节中得到168位加奇偶校验。这使得DESede的密码始终采用24字节表示,而不管它是K1、K2、K3还是K1、K2、K1,甚至是K1、K1、K1。当然,由于在中间相遇,力量较小;看见
byte[] encrypted = cipher.doFinal(secKey.getEncoded());