3DES加解密报错 Invalid key length: 32 bytes

开发过程中用到3DES对敏感信息进行加密,祕钥用的是32位的,报出如下异常

Exception in thread "main" java.security.InvalidKeyException: Invalid key length: 32 bytes
    at com.sun.crypto.provider.DESedeCipher.engineGetKeySize(DESedeCipher.java:370)
    at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1067)
    at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1025)
    at javax.crypto.Cipher.implInit(Cipher.java:801)
    at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
    at javax.crypto.Cipher.init(Cipher.java:1249)
    at javax.crypto.Cipher.init(Cipher.java:1186)
    at com.adtec.des.des_temp.encryptThreeDESECB(des_temp.java:33)
    at com.adtec.des.des_temp.main(des_temp.java:61)

加密代码如下:

 /** * * @param src 加密内容 * @param key 祕钥 32位长度 * @return 加密后的内容 * @throws Exception */
    public static byte[] encryptThreeDESECB(final String src, final String key) throws Exception {

        SecretKey securekey = new SecretKeySpec(key.getBytes(), "DESede");

        final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, securekey);
        final byte[] b = cipher.doFinal(src.getBytes());

        return b;

    }

测试用例

public static void main(String[] args) throws Exception {
        String key = "11112222333344445555666677778888";
        // 加密流程
        String telePhone = "123456";
        byte[] telePhone_encrypt = null;
        telePhone_encrypt = encryptThreeDESECB(URLEncoder.encode(telePhone, "UTF-8"), key);
        System.out.println(new String(telePhone_encrypt));
        // 解密流程
        String tele_decrypt = decryptThreeDESECB(telePhone_encrypt, key);
        System.out.println("解密:" + tele_decrypt);
    }

感觉不应该有问题啊???
试着将祕钥长度减为24位
《3DES加解密报错 Invalid key length: 32 bytes》

这就可以了,但这是不可以的,系统不是我一个人操作,我用前24位加密,别人不知道,用后24位解密,就会导致结果不一致,无法解密。

知道我看见了另外一个方法:

/** * * @param src 加密内容 * @param key 祕钥 32位长度 * @return 加密后的内容 * @throws Exception */
    public static byte[] encryptThreeDESECB(final String src, final String key) throws Exception {
        final DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));

        final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
        final SecretKey securekey = keyFactory.generateSecret(dks);
        final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, securekey);
        final byte[] b = cipher.doFinal(src.getBytes());

        return b;

    }

其他的一模一样

《3DES加解密报错 Invalid key length: 32 bytes》

解密代码也一样

 /** * * @param telePhone_encrypt 密文 * @param key 祕钥 32位长度 * @return 解密后明文 * @throws Exception */
    public static String decryptThreeDESECB(byte[] telePhone_encrypt, final String key) throws Exception {
        // --解密的key
        final DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));
        final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
        final SecretKey securekey = keyFactory.generateSecret(dks);

        // --Chipher对象解密
        final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, securekey);
        final byte[] retByte = cipher.doFinal(telePhone_encrypt);

        return new String(retByte);
    }

仔细看了一下有一行代码有重大嫌疑

 final DESedeKeySpec dks = new DESedeKeySpec(key.getBytes("UTF-8"));

看一眼源码
《3DES加解密报错 Invalid key length: 32 bytes》

大概说的是,它用前24位构建DESedeKeySpec对象

再看看其DESedeKeySpec内部是如何实现的,打卡jdk的源码

/** * Uses the first 24 bytes in <code>key</code>, beginning at * <code>offset</code>, as the DES-EDE key * * @param key the buffer with the DES-EDE key * @param offset the offset in <code>key</code>, where the DES-EDE key * starts * * @exception InvalidKeyException if the given key has a wrong size */
DESedeKey(byte[] key, int offset) throws InvalidKeyException {

    if (key==null || ((key.length-offset)<DESedeKeySpec.DES_EDE_KEY_LEN)) {
        throw new InvalidKeyException("Wrong key size");
    }
    this.key = new byte[DESedeKeySpec.DES_EDE_KEY_LEN];
    System.arraycopy(key, offset, this.key, 0,
                     DESedeKeySpec.DES_EDE_KEY_LEN);
    DESKeyGenerator.setParityBit(this.key, 0);
    DESKeyGenerator.setParityBit(this.key, 8);
    DESKeyGenerator.setParityBit(this.key, 16);

    // Use the cleaner to zero the key when no longer referenced
    final byte[] k = this.key;
    CleanerFactory.cleaner().register(this,
            () -> java.util.Arrays.fill(k, (byte)0x00));
}

看到一个关键的东西:DESedeKeySpec.DES_EDE_KEY_LEN
《3DES加解密报错 Invalid key length: 32 bytes》

果然长度是24

从底层源码

if (key==null || ((key.length-offset)<DESedeKeySpec.DES_EDE_KEY_LEN)) {
        throw new InvalidKeyException("Wrong key size");
    }

可以看出,如果祕钥为空或者小于24位会抛出Wrong key size,试一下
《3DES加解密报错 Invalid key length: 32 bytes》

再试一下超过32位的
《3DES加解密报错 Invalid key length: 32 bytes》

至此就可以发现了,后面这张加密方法,祕钥长度不能小于24,可以大于24,但是其实现的时候只截取了前24位

总结,推荐使用第二中加密方法,因为是底层截取的,不是我们手动截取的,无论是加密方还是解密方都遵循这个规则

点赞