欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-15 16:56:43
...

开发过程中用到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位

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